├── .eslintrc.json ├── .prettierrc ├── commitlint.config.js ├── public ├── favicon.ico ├── assets │ ├── images │ │ ├── PR.png │ │ ├── new.png │ │ ├── home.png │ │ ├── card_bg.png │ │ ├── logo-ort.jpg │ │ ├── notebook.png │ │ ├── Logo-anim.gif │ │ ├── background.png │ │ ├── new_bottom.png │ │ ├── home_create.png │ │ ├── cloudinary_icon.png │ │ ├── postgres │ │ │ ├── table.png │ │ │ ├── storage.png │ │ │ ├── connection.png │ │ │ ├── new_server.png │ │ │ └── server_data.png │ │ ├── new_bottom_button.png │ │ ├── soquetic │ │ │ ├── hardware.png │ │ │ ├── postEvent.png │ │ │ └── soqueticDiagrama.png │ │ ├── css.svg │ │ ├── html.svg │ │ ├── prisma.svg │ │ ├── manifest.webmanifest │ │ ├── js.svg │ │ ├── postgres.svg │ │ ├── sql.svg │ │ ├── php.svg │ │ ├── python.svg │ │ ├── node.svg │ │ ├── express.svg │ │ ├── git.svg │ │ ├── nextjs.svg │ │ ├── bash.svg │ │ ├── cloudinary.svg │ │ ├── react.svg │ │ ├── soquetic.svg │ │ ├── mysql2.svg │ │ └── ino.svg │ ├── icons │ │ ├── maskable.png │ │ ├── GitHubLogo.png │ │ ├── icon-48x48.png │ │ ├── icon-72x72.png │ │ ├── icon-96x96.png │ │ ├── icon-128x128.png │ │ ├── icon-144x144.png │ │ ├── icon-152x152.png │ │ ├── icon-192x192.png │ │ ├── icon-384x384.png │ │ ├── icon-512x512.png │ │ ├── Moon.svg │ │ └── Sun.svg │ └── fontawesome │ │ ├── webfonts │ │ ├── fa-brands-400.ttf │ │ ├── fa-regular-400.ttf │ │ ├── fa-solid-900.ttf │ │ ├── fa-solid-900.woff2 │ │ ├── fa-brands-400.woff2 │ │ ├── fa-regular-400.woff2 │ │ ├── fa-v4compatibility.ttf │ │ └── fa-v4compatibility.woff2 │ │ ├── scss │ │ ├── _fixed-width.scss │ │ ├── _icons.scss │ │ ├── _sizing.scss │ │ ├── v4-shims.scss │ │ ├── _screen-reader.scss │ │ ├── _list.scss │ │ ├── fontawesome.scss │ │ ├── _rotated-flipped.scss │ │ ├── _stacked.scss │ │ ├── _bordered-pulled.scss │ │ ├── solid.scss │ │ ├── regular.scss │ │ ├── _core.scss │ │ ├── brands.scss │ │ ├── _mixins.scss │ │ ├── _functions.scss │ │ └── _animated.scss │ │ └── css │ │ ├── solid.min.css │ │ ├── regular.min.css │ │ ├── solid.css │ │ ├── regular.css │ │ ├── v5-font-face.min.css │ │ ├── v5-font-face.css │ │ ├── v4-font-face.min.css │ │ └── v4-font-face.css ├── manifest.json └── download │ └── socket.js ├── @types └── index.d.ts ├── .husky └── commit-msg ├── postcss.config.js ├── next-env.d.ts ├── components ├── layout │ ├── Background.tsx │ ├── FloatingButtons.tsx │ ├── Section.tsx │ └── Header.tsx ├── Cheatsheet.module.scss └── utils │ ├── Loading.tsx │ ├── Glassbox.tsx │ ├── FloatingButton.tsx │ ├── ThemeSwitch.tsx │ └── Terminal │ ├── Autocomplete.tsx │ ├── Terminal.tsx │ ├── Command.tsx │ └── useCommandList.tsx ├── next.config.js ├── tsconfig.json ├── contexts ├── LoadingContext.tsx ├── ThemeContext.tsx └── TerminalContext.tsx ├── .gitignore ├── hooks └── useWindowSize.tsx ├── LICENSE ├── .markdownlint.json ├── pages ├── _document.tsx ├── _app.tsx ├── cheatsheet │ └── [slug].tsx └── index.tsx ├── package.json ├── highlighters └── prisma.js ├── tailwind.config.js ├── cheatsheets ├── node.md ├── mysql2.md ├── bash.md ├── git.md ├── postgres.md ├── cloudinary-multer.md ├── react.md └── express.md └── README.md /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next"] 3 | } 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "useTabs": false 4 | } 5 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ['@commitlint/config-conventional'] }; -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /@types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.svg" { 2 | const content: any; 3 | export default content; 4 | } 5 | -------------------------------------------------------------------------------- /public/assets/images/PR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/PR.png -------------------------------------------------------------------------------- /public/assets/images/new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/new.png -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx.cmd commitlint --edit "${1}" 5 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/assets/images/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/home.png -------------------------------------------------------------------------------- /public/assets/icons/maskable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/icons/maskable.png -------------------------------------------------------------------------------- /public/assets/images/card_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/card_bg.png -------------------------------------------------------------------------------- /public/assets/images/logo-ort.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/logo-ort.jpg -------------------------------------------------------------------------------- /public/assets/images/notebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/notebook.png -------------------------------------------------------------------------------- /public/assets/icons/GitHubLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/icons/GitHubLogo.png -------------------------------------------------------------------------------- /public/assets/icons/icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/icons/icon-48x48.png -------------------------------------------------------------------------------- /public/assets/icons/icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/icons/icon-72x72.png -------------------------------------------------------------------------------- /public/assets/icons/icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/icons/icon-96x96.png -------------------------------------------------------------------------------- /public/assets/images/Logo-anim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/Logo-anim.gif -------------------------------------------------------------------------------- /public/assets/images/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/background.png -------------------------------------------------------------------------------- /public/assets/images/new_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/new_bottom.png -------------------------------------------------------------------------------- /public/assets/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/icons/icon-128x128.png -------------------------------------------------------------------------------- /public/assets/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/icons/icon-144x144.png -------------------------------------------------------------------------------- /public/assets/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/icons/icon-152x152.png -------------------------------------------------------------------------------- /public/assets/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/icons/icon-192x192.png -------------------------------------------------------------------------------- /public/assets/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/icons/icon-384x384.png -------------------------------------------------------------------------------- /public/assets/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/icons/icon-512x512.png -------------------------------------------------------------------------------- /public/assets/images/home_create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/home_create.png -------------------------------------------------------------------------------- /public/assets/images/cloudinary_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/cloudinary_icon.png -------------------------------------------------------------------------------- /public/assets/images/postgres/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/postgres/table.png -------------------------------------------------------------------------------- /public/assets/images/new_bottom_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/new_bottom_button.png -------------------------------------------------------------------------------- /public/assets/images/postgres/storage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/postgres/storage.png -------------------------------------------------------------------------------- /public/assets/images/soquetic/hardware.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/soquetic/hardware.png -------------------------------------------------------------------------------- /public/assets/images/soquetic/postEvent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/soquetic/postEvent.png -------------------------------------------------------------------------------- /public/assets/images/postgres/connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/postgres/connection.png -------------------------------------------------------------------------------- /public/assets/images/postgres/new_server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/postgres/new_server.png -------------------------------------------------------------------------------- /public/assets/images/postgres/server_data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/postgres/server_data.png -------------------------------------------------------------------------------- /public/assets/images/soquetic/soqueticDiagrama.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/images/soquetic/soqueticDiagrama.png -------------------------------------------------------------------------------- /public/assets/fontawesome/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/fontawesome/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /public/assets/fontawesome/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/fontawesome/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /public/assets/fontawesome/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/fontawesome/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /public/assets/fontawesome/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/fontawesome/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /public/assets/fontawesome/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/fontawesome/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /public/assets/fontawesome/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/fontawesome/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /public/assets/fontawesome/webfonts/fa-v4compatibility.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/fontawesome/webfonts/fa-v4compatibility.ttf -------------------------------------------------------------------------------- /public/assets/fontawesome/webfonts/fa-v4compatibility.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachovigilante/cheatsheets/HEAD/public/assets/fontawesome/webfonts/fa-v4compatibility.woff2 -------------------------------------------------------------------------------- /public/assets/fontawesome/scss/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // fixed-width icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-fw { 5 | text-align: center; 6 | width: $fa-fw-width; 7 | } 8 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /components/layout/Background.tsx: -------------------------------------------------------------------------------- 1 | const Background = () => { 2 | return ( 3 |
4 | ); 5 | }; 6 | 7 | export default Background; 8 | -------------------------------------------------------------------------------- /public/assets/fontawesome/scss/_icons.scss: -------------------------------------------------------------------------------- 1 | // specific icon class definition 2 | // ------------------------- 3 | 4 | /* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen 5 | readers do not read off random characters that represent icons */ 6 | 7 | @each $name, $icon in $fa-icons { 8 | .#{$fa-css-prefix}-#{$name}::before { content: unquote("\"#{ $icon }\""); } 9 | } 10 | -------------------------------------------------------------------------------- /public/assets/fontawesome/scss/_sizing.scss: -------------------------------------------------------------------------------- 1 | // sizing icons 2 | // ------------------------- 3 | 4 | // literal magnification scale 5 | @for $i from 1 through 10 { 6 | .#{$fa-css-prefix}-#{$i}x { 7 | font-size: $i * 1em; 8 | } 9 | } 10 | 11 | // step-based scale (with alignment) 12 | @each $size, $value in $fa-sizes { 13 | .#{$fa-css-prefix}-#{$size} { 14 | @include fa-size($value); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /public/assets/fontawesome/scss/v4-shims.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 6.1.2 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | * Copyright 2022 Fonticons, Inc. 5 | */ 6 | // V4 shims compile (Web Fonts-based) 7 | // ------------------------- 8 | 9 | @import 'functions'; 10 | @import 'variables'; 11 | @import 'shims'; 12 | -------------------------------------------------------------------------------- /public/assets/fontawesome/scss/_screen-reader.scss: -------------------------------------------------------------------------------- 1 | // screen-reader utilities 2 | // ------------------------- 3 | 4 | // only display content to screen readers 5 | .sr-only, 6 | .#{$fa-css-prefix}-sr-only { 7 | @include fa-sr-only; 8 | } 9 | 10 | // use in conjunction with .sr-only to only display content when it's focused 11 | .sr-only-focusable, 12 | .#{$fa-css-prefix}-sr-only-focusable { 13 | @include fa-sr-only-focusable; 14 | } 15 | -------------------------------------------------------------------------------- /components/layout/FloatingButtons.tsx: -------------------------------------------------------------------------------- 1 | import styles from "./FloatingButtons.module.scss"; 2 | 3 | const FloatingButtons = ({ children }: { children: React.ReactNode }) => { 4 | return ( 5 |
10 | {children} 11 |
12 | ); 13 | }; 14 | 15 | export default FloatingButtons; 16 | -------------------------------------------------------------------------------- /public/assets/fontawesome/scss/_list.scss: -------------------------------------------------------------------------------- 1 | // icons in a list 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-ul { 5 | list-style-type: none; 6 | margin-left: var(--#{$fa-css-prefix}-li-margin, #{$fa-li-margin}); 7 | padding-left: 0; 8 | 9 | > li { position: relative; } 10 | } 11 | 12 | .#{$fa-css-prefix}-li { 13 | left: calc(var(--#{$fa-css-prefix}-li-width, #{$fa-li-width}) * -1); 14 | position: absolute; 15 | text-align: center; 16 | width: var(--#{$fa-css-prefix}-li-width, #{$fa-li-width}); 17 | line-height: inherit; 18 | } 19 | -------------------------------------------------------------------------------- /public/assets/images/css.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/fontawesome/css/solid.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 6.1.2 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | * Copyright 2022 Fonticons, Inc. 5 | */ 6 | :host,:root{--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-family:"Font Awesome 6 Free";font-weight:900} -------------------------------------------------------------------------------- /public/assets/fontawesome/css/regular.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 6.1.2 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | * Copyright 2022 Fonticons, Inc. 5 | */ 6 | :host,:root{--fa-font-regular:normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}.fa-regular,.far{font-family:"Font Awesome 6 Free";font-weight:400} -------------------------------------------------------------------------------- /public/assets/images/html.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | 3 | const withPWA = require("next-pwa"); 4 | 5 | const nextConfig = { 6 | webpack: (config) => { 7 | config.module.rules.push({ 8 | test: /\.svg$/, 9 | exclude: /node_modules/, 10 | use: ["@svgr/webpack"], 11 | }); 12 | return config; 13 | }, 14 | pwa: { 15 | dest: "public", 16 | register: true, 17 | skipWaiting: true, 18 | disable: process.env.NODE_ENV === "development", 19 | }, 20 | reactStrictMode: true, 21 | swcMinify: true, 22 | }; 23 | 24 | module.exports = withPWA(nextConfig); 25 | -------------------------------------------------------------------------------- /public/assets/images/prisma.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/fontawesome/scss/fontawesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 6.1.2 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | * Copyright 2022 Fonticons, Inc. 5 | */ 6 | // Font Awesome core compile (Web Fonts-based) 7 | // ------------------------- 8 | 9 | @import 'functions'; 10 | @import 'variables'; 11 | @import 'mixins'; 12 | @import 'core'; 13 | @import 'sizing'; 14 | @import 'fixed-width'; 15 | @import 'list'; 16 | @import 'bordered-pulled'; 17 | @import 'animated'; 18 | @import 'rotated-flipped'; 19 | @import 'stacked'; 20 | @import 'icons'; 21 | @import 'screen-reader'; 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": false, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "incremental": true, 15 | "esModuleInterop": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "jsx": "preserve" 21 | }, 22 | "include": [ 23 | "next-env.d.ts", 24 | "**/*.ts", 25 | "**/*.tsx" 26 | ], 27 | "exclude": [ 28 | "node_modules" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /contexts/LoadingContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useState, Dispatch, SetStateAction } from "react"; 2 | 3 | type LoadingContextType = { 4 | loading: boolean; 5 | setLoading: Dispatch>; 6 | }; 7 | 8 | export const LoadingContext = createContext(null as null | LoadingContextType); 9 | 10 | export const LoadingProvider = ({ 11 | children, 12 | }: { 13 | children: React.ReactNode; 14 | }) => { 15 | const [loading, setLoading] = useState(false); 16 | 17 | return ( 18 | <> 19 | 20 | {children} 21 | 22 | 23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /public/assets/fontawesome/css/solid.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 6.1.2 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | * Copyright 2022 Fonticons, Inc. 5 | */ 6 | :root, :host { 7 | --fa-font-solid: normal 900 1em/1 "Font Awesome 6 Free"; } 8 | 9 | @font-face { 10 | font-family: 'Font Awesome 6 Free'; 11 | font-style: normal; 12 | font-weight: 900; 13 | font-display: block; 14 | src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); } 15 | 16 | .fas, 17 | .fa-solid { 18 | font-family: 'Font Awesome 6 Free'; 19 | font-weight: 900; } 20 | -------------------------------------------------------------------------------- /public/assets/fontawesome/css/regular.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 6.1.2 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | * Copyright 2022 Fonticons, Inc. 5 | */ 6 | :root, :host { 7 | --fa-font-regular: normal 400 1em/1 "Font Awesome 6 Free"; } 8 | 9 | @font-face { 10 | font-family: 'Font Awesome 6 Free'; 11 | font-style: normal; 12 | font-weight: 400; 13 | font-display: block; 14 | src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); } 15 | 16 | .far, 17 | .fa-regular { 18 | font-family: 'Font Awesome 6 Free'; 19 | font-weight: 400; } 20 | -------------------------------------------------------------------------------- /public/assets/fontawesome/scss/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // rotating + flipping icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { 5 | transform: rotate(90deg); 6 | } 7 | 8 | .#{$fa-css-prefix}-rotate-180 { 9 | transform: rotate(180deg); 10 | } 11 | 12 | .#{$fa-css-prefix}-rotate-270 { 13 | transform: rotate(270deg); 14 | } 15 | 16 | .#{$fa-css-prefix}-flip-horizontal { 17 | transform: scale(-1, 1); 18 | } 19 | 20 | .#{$fa-css-prefix}-flip-vertical { 21 | transform: scale(1, -1); 22 | } 23 | 24 | .#{$fa-css-prefix}-flip-both, 25 | .#{$fa-css-prefix}-flip-horizontal.#{$fa-css-prefix}-flip-vertical { 26 | transform: scale(-1, -1); 27 | } 28 | 29 | .#{$fa-css-prefix}-rotate-by { 30 | transform: rotate(var(--#{$fa-css-prefix}-rotate-angle, none)); 31 | } 32 | -------------------------------------------------------------------------------- /public/assets/fontawesome/scss/_stacked.scss: -------------------------------------------------------------------------------- 1 | // stacking icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | display: inline-block; 6 | height: 2em; 7 | line-height: 2em; 8 | position: relative; 9 | vertical-align: $fa-stack-vertical-align; 10 | width: $fa-stack-width; 11 | } 12 | 13 | .#{$fa-css-prefix}-stack-1x, 14 | .#{$fa-css-prefix}-stack-2x { 15 | left: 0; 16 | position: absolute; 17 | text-align: center; 18 | width: 100%; 19 | z-index: var(--#{$fa-css-prefix}-stack-z-index, #{$fa-stack-z-index}); 20 | } 21 | 22 | .#{$fa-css-prefix}-stack-1x { 23 | line-height: inherit; 24 | } 25 | 26 | .#{$fa-css-prefix}-stack-2x { 27 | font-size: 2em; 28 | } 29 | 30 | .#{$fa-css-prefix}-inverse { 31 | color: var(--#{$fa-css-prefix}-inverse, #{$fa-inverse}); 32 | } 33 | -------------------------------------------------------------------------------- /.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 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | .pnpm-debug.log* 28 | 29 | # local env files 30 | .env*.local 31 | 32 | # vercel 33 | .vercel 34 | 35 | # Service Worker 36 | **/public/*.worker.js 37 | **/public/*.worker.js.map 38 | **/public/sw.js 39 | **/public/sw.js.map 40 | **/public/workbox-*.js 41 | **/public/worker-*.js 42 | **/public/workbox-*.js.map 43 | **/public/worker-*.js.map 44 | 45 | # Anim 46 | **/anim/ 47 | 48 | # Download 49 | **/download/*.pdf -------------------------------------------------------------------------------- /components/layout/Section.tsx: -------------------------------------------------------------------------------- 1 | import { twMerge } from "tailwind-merge"; 2 | 3 | type SectionProps = { 4 | id?: string; 5 | children?: React.ReactNode; 6 | className?: string; 7 | row?: boolean; 8 | }; 9 | 10 | const Section = ({ children, className, row, id }: SectionProps) => { 11 | return ( 12 |
16 |
22 | {children} 23 |
24 |
25 | ); 26 | }; 27 | 28 | export default Section; 29 | -------------------------------------------------------------------------------- /components/Cheatsheet.module.scss: -------------------------------------------------------------------------------- 1 | .cheatsheet { 2 | display: flex; 3 | flex-direction: column; 4 | width: 250px; 5 | border-radius: 10px; 6 | box-shadow: var(--shadow); 7 | margin: 20px; 8 | background-color: var(--sec-bg-color); 9 | text-decoration: none; 10 | 11 | 12 | img { 13 | height: 150px; 14 | width: 100%; 15 | border-radius: 10px 10px 0px 0px; 16 | background-color: var(--font-color); 17 | } 18 | 19 | span { 20 | width: 100%; 21 | padding: 17px; 22 | color: white; 23 | font-size: 20px; 24 | } 25 | 26 | &:hover { 27 | box-shadow: var(--hover-shadow); 28 | 29 | span { 30 | color: var(--acc-color); 31 | transition: 0.2s; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /components/utils/Loading.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import { useContext } from "react"; 3 | import { LoadingContext } from "../../contexts/LoadingContext"; 4 | import { twMerge } from "tailwind-merge"; 5 | 6 | const Loading = () => { 7 | const { loading } = useContext(LoadingContext); 8 | 9 | return ( 10 |
11 | 16 |

Cargando...

17 |
18 | ); 19 | }; 20 | 21 | export default Loading; 22 | -------------------------------------------------------------------------------- /public/assets/fontawesome/css/v5-font-face.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 6.1.2 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | * Copyright 2022 Fonticons, Inc. 5 | */ 6 | @font-face{font-family:"Font Awesome 5 Brands";font-display:block;font-weight:400;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")} -------------------------------------------------------------------------------- /public/assets/fontawesome/scss/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // bordered + pulled icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | border-color: var(--#{$fa-css-prefix}-border-color, #{$fa-border-color}); 6 | border-radius: var(--#{$fa-css-prefix}-border-radius, #{$fa-border-radius}); 7 | border-style: var(--#{$fa-css-prefix}-border-style, #{$fa-border-style}); 8 | border-width: var(--#{$fa-css-prefix}-border-width, #{$fa-border-width}); 9 | padding: var(--#{$fa-css-prefix}-border-padding, #{$fa-border-padding}); 10 | } 11 | 12 | .#{$fa-css-prefix}-pull-left { 13 | float: left; 14 | margin-right: var(--#{$fa-css-prefix}-pull-margin, #{$fa-pull-margin}); 15 | } 16 | 17 | .#{$fa-css-prefix}-pull-right { 18 | float: right; 19 | margin-left: var(--#{$fa-css-prefix}-pull-margin, #{$fa-pull-margin}); 20 | } 21 | -------------------------------------------------------------------------------- /public/assets/fontawesome/scss/solid.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 6.1.2 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | * Copyright 2022 Fonticons, Inc. 5 | */ 6 | @import 'functions'; 7 | @import 'variables'; 8 | 9 | :root, :host { 10 | --#{$fa-css-prefix}-font-solid: normal 900 1em/1 "#{ $fa-style-family }"; 11 | } 12 | 13 | @font-face { 14 | font-family: 'Font Awesome 6 Free'; 15 | font-style: normal; 16 | font-weight: 900; 17 | font-display: $fa-font-display; 18 | src: url('#{$fa-font-path}/fa-solid-900.woff2') format('woff2'), 19 | url('#{$fa-font-path}/fa-solid-900.ttf') format('truetype'); 20 | } 21 | 22 | .fas, 23 | .#{$fa-css-prefix}-solid { 24 | font-family: 'Font Awesome 6 Free'; 25 | font-weight: 900; 26 | } 27 | -------------------------------------------------------------------------------- /hooks/useWindowSize.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | 3 | const getCurrentSize = () => { 4 | if (typeof window !== "undefined") { 5 | return { 6 | width: window.innerWidth, 7 | height: window.innerHeight, 8 | }; 9 | } 10 | return { 11 | width: 0, 12 | height: 0, 13 | }; 14 | }; 15 | 16 | const useWindowSize = () => { 17 | const [windowSize, setWindowSize] = useState(getCurrentSize); 18 | 19 | useEffect(() => { 20 | window.addEventListener("resize", () => 21 | setWindowSize(getCurrentSize()) 22 | ); 23 | 24 | return () => { 25 | window.removeEventListener("resize", () => 26 | setWindowSize(getCurrentSize()) 27 | ); 28 | }; 29 | }, []); 30 | 31 | return windowSize; 32 | }; 33 | -------------------------------------------------------------------------------- /public/assets/fontawesome/scss/regular.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 6.1.2 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | * Copyright 2022 Fonticons, Inc. 5 | */ 6 | @import 'functions'; 7 | @import 'variables'; 8 | 9 | :root, :host { 10 | --#{$fa-css-prefix}-font-regular: normal 400 1em/1 "#{ $fa-style-family }"; 11 | } 12 | 13 | @font-face { 14 | font-family: 'Font Awesome 6 Free'; 15 | font-style: normal; 16 | font-weight: 400; 17 | font-display: $fa-font-display; 18 | src: url('#{$fa-font-path}/fa-regular-400.woff2') format('woff2'), 19 | url('#{$fa-font-path}/fa-regular-400.ttf') format('truetype'); 20 | } 21 | 22 | .far, 23 | .#{$fa-css-prefix}-regular { 24 | font-family: 'Font Awesome 6 Free'; 25 | font-weight: 400; 26 | } 27 | -------------------------------------------------------------------------------- /public/assets/images/manifest.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "theme_color": "#f69435", 3 | "background_color": "#f69435", 4 | "display": "browser", 5 | "scope": "/", 6 | "start_url": "/", 7 | "name": "fdg", 8 | "short_name": "fdgh", 9 | "icons": [ 10 | { 11 | "src": "/icon-192x192.png", 12 | "sizes": "192x192", 13 | "type": "image/png" 14 | }, 15 | { 16 | "src": "/icon-256x256.png", 17 | "sizes": "256x256", 18 | "type": "image/png" 19 | }, 20 | { 21 | "src": "/icon-384x384.png", 22 | "sizes": "384x384", 23 | "type": "image/png" 24 | }, 25 | { 26 | "src": "/icon-512x512.png", 27 | "sizes": "512x512", 28 | "type": "image/png" 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /contexts/ThemeContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | import Head from "next/head"; 3 | import { useLocalStorage } from "usehooks-ts"; 4 | 5 | export type ThemeType = "light" | "dark"; 6 | 7 | type ThemeContextType = { 8 | theme: ThemeType; 9 | setTheme: any; 10 | }; 11 | 12 | export const ThemeContext = createContext(null as null | ThemeContextType); 13 | 14 | export const ThemeProvider = ({ children }: { children: React.ReactNode }) => { 15 | const [theme, setTheme] = useLocalStorage("theme", "light"); 16 | 17 | return ( 18 | <> 19 | 20 |
21 | {children} 22 |
23 |
24 | 25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /public/assets/fontawesome/scss/_core.scss: -------------------------------------------------------------------------------- 1 | // base icon class definition 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix} { 5 | font-family: var(--#{$fa-css-prefix}-style-family, '#{$fa-style-family}'); 6 | font-weight: var(--#{$fa-css-prefix}-style, #{$fa-style}); 7 | } 8 | 9 | .#{$fa-css-prefix}, 10 | .fas, 11 | .#{$fa-css-prefix}-solid, 12 | .far, 13 | .#{$fa-css-prefix}-regular, 14 | .fal, 15 | .#{$fa-css-prefix}-light, 16 | .fat, 17 | .#{$fa-css-prefix}-thin, 18 | .fad, 19 | .#{$fa-css-prefix}-duotone, 20 | .fab, 21 | .#{$fa-css-prefix}-brands { 22 | -moz-osx-font-smoothing: grayscale; 23 | -webkit-font-smoothing: antialiased; 24 | display: var(--#{$fa-css-prefix}-display, #{$fa-display}); 25 | font-style: normal; 26 | font-variant: normal; 27 | line-height: 1; 28 | text-rendering: auto; 29 | } 30 | 31 | %fa-icon { 32 | @include fa-icon; 33 | } 34 | -------------------------------------------------------------------------------- /public/assets/fontawesome/css/v5-font-face.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 6.1.2 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | * Copyright 2022 Fonticons, Inc. 5 | */ 6 | @font-face { 7 | font-family: "Font Awesome 5 Brands"; 8 | font-display: block; 9 | font-weight: 400; 10 | src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); } 11 | 12 | @font-face { 13 | font-family: "Font Awesome 5 Free"; 14 | font-display: block; 15 | font-weight: 900; 16 | src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); } 17 | 18 | @font-face { 19 | font-family: "Font Awesome 5 Free"; 20 | font-display: block; 21 | font-weight: 400; 22 | src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); } 23 | -------------------------------------------------------------------------------- /public/assets/fontawesome/scss/brands.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 6.1.2 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | * Copyright 2022 Fonticons, Inc. 5 | */ 6 | @import 'functions'; 7 | @import 'variables'; 8 | 9 | :root, :host { 10 | --#{$fa-css-prefix}-font-brands: normal 400 1em/1 "Font Awesome 6 Brands"; 11 | } 12 | 13 | @font-face { 14 | font-family: 'Font Awesome 6 Brands'; 15 | font-style: normal; 16 | font-weight: 400; 17 | font-display: $fa-font-display; 18 | src: url('#{$fa-font-path}/fa-brands-400.woff2') format('woff2'), 19 | url('#{$fa-font-path}/fa-brands-400.ttf') format('truetype'); 20 | } 21 | 22 | .fab, 23 | .#{$fa-css-prefix}-brands { 24 | font-family: 'Font Awesome 6 Brands'; 25 | font-weight: 400; 26 | } 27 | 28 | @each $name, $icon in $fa-brand-icons { 29 | .#{$fa-css-prefix}-#{$name}:before { content: unquote("\"#{ $icon }\""); } 30 | } 31 | -------------------------------------------------------------------------------- /public/assets/images/js.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 nachovigilante 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /public/assets/images/postgres.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "MD013": false, 3 | "no-inline-html": { 4 | "allowed_elements": [ 5 | "div", 6 | "h1", 7 | "h2", 8 | "h3", 9 | "h4", 10 | "h5", 11 | "h6", 12 | "p", 13 | "ul", 14 | "ol", 15 | "li", 16 | "table", 17 | "thead", 18 | "tbody", 19 | "tfoot", 20 | "tr", 21 | "th", 22 | "td", 23 | "img", 24 | "hr", 25 | "br", 26 | "a", 27 | "i", 28 | "u", 29 | "code", 30 | "span", 31 | "small", 32 | "form", 33 | "label", 34 | "input", 35 | "textarea", 36 | "button", 37 | "select", 38 | "option", 39 | "canvas", 40 | "audio", 41 | "video", 42 | "script", 43 | "style", 44 | "link", 45 | "meta", 46 | "title", 47 | "head", 48 | "body", 49 | "html" 50 | ] 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /components/utils/Glassbox.tsx: -------------------------------------------------------------------------------- 1 | import { twMerge } from "tailwind-merge"; 2 | 3 | const Glassbox = ({ 4 | children, 5 | className, 6 | containerClassName, 7 | ...props 8 | }: { 9 | children: React.ReactNode; 10 | className?: string; 11 | containerClassName?: string; 12 | props?: any; 13 | }) => { 14 | return ( 15 |
16 |
22 | {children} 23 |
24 |
25 | ); 26 | }; 27 | 28 | export const TitledGlassBox = ({ 29 | title, 30 | children, 31 | className, 32 | }: { 33 | title: string; 34 | children: React.ReactNode; 35 | className?: string; 36 | }) => { 37 | return ( 38 | 39 |

40 | {title} 41 |

42 | {children} 43 |
44 | ); 45 | }; 46 | 47 | export default Glassbox; 48 | -------------------------------------------------------------------------------- /components/utils/FloatingButton.tsx: -------------------------------------------------------------------------------- 1 | import { twMerge } from "tailwind-merge"; 2 | 3 | const defaultClassName = 4 | "bg-accent rounded-full xl:w-16 xl:h-16 w-12 h-12 flex items-center justify-center text-white xl:text-2xl text-xl shadow-default transition-all duration-150 ease-in-out cursor-pointer border-none hover:bg-accent-hover active:bg-accent-active focus:outline-none"; 5 | 6 | const FloatingButton = ({ 7 | className, 8 | onClick, 9 | children, 10 | ariaLabel, 11 | link, 12 | download, 13 | }: { 14 | className?: string; 15 | onClick?: () => void; 16 | children?: React.ReactNode; 17 | ariaLabel?: string; 18 | link?: string; 19 | download?: boolean; 20 | }) => { 21 | return link ? ( 22 | 28 | {children} 29 | 30 | ) : ( 31 | 38 | ); 39 | }; 40 | 41 | export default FloatingButton; 42 | -------------------------------------------------------------------------------- /public/assets/images/sql.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/images/php.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from "next/document"; 2 | 3 | const Document = () => { 4 | return ( 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 | 17 | 21 | 27 | 28 | 29 |
30 | 31 | 32 | 33 | ); 34 | }; 35 | 36 | export default Document; 37 | -------------------------------------------------------------------------------- /public/assets/icons/Moon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "prod": "next build && next start", 10 | "lint": "next lint", 11 | "pretty": "prettier --write **/*.{js,jsx,ts,tsx,json,css,scss}", 12 | "prepare": "husky install", 13 | "pdf": "node ./scripts/generatePDF.js", 14 | "unix": "sed -i 's/.cmd//g' .husky/commit-msg && git update-index --assume-unchanged .husky/commit-msg && chmod ug+x .husky/*" 15 | }, 16 | "dependencies": { 17 | "@svgr/webpack": "^6.3.1", 18 | "fs": "^0.0.1-security", 19 | "gray-matter": "^4.0.3", 20 | "highlight.js": "^11.6.0", 21 | "katex": "^0.16.0", 22 | "marked": "^4.0.18", 23 | "marked-katex": "^0.3.8", 24 | "next": "12.2.3", 25 | "next-pwa": "5.5.4", 26 | "react": "18.2.0", 27 | "react-dom": "18.2.0", 28 | "react-github-btn": "^1.4.0", 29 | "sass": "^1.54.0", 30 | "tailwind-merge": "^1.10.0", 31 | "usehooks-ts": "^2.6.0" 32 | }, 33 | "devDependencies": { 34 | "@commitlint/cli": "^17.1.2", 35 | "@commitlint/config-conventional": "^17.1.0", 36 | "@types/node": "^18.6.3", 37 | "@types/react": "^18.0.15", 38 | "autoprefixer": "^10.4.14", 39 | "commitlint": "^17.1.2", 40 | "eslint": "8.20.0", 41 | "eslint-config-next": "12.2.3", 42 | "husky": "^8.0.0", 43 | "postcss": "^8.4.21", 44 | "tailwindcss": "^3.2.7", 45 | "typescript": "^4.7.4" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /public/assets/fontawesome/css/v4-font-face.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 6.1.2 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | * Copyright 2022 Fonticons, Inc. 5 | */ 6 | @font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype");unicode-range:u+f003,u+f006,u+f014,u+f016-f017,u+f01a-f01b,u+f01d,u+f022,u+f03e,u+f044,u+f046,u+f05c-f05d,u+f06e,u+f070,u+f087-f088,u+f08a,u+f094,u+f096-f097,u+f09d,u+f0a0,u+f0a2,u+f0a4-f0a7,u+f0c5,u+f0c7,u+f0e5-f0e6,u+f0eb,u+f0f6-f0f8,u+f10c,u+f114-f115,u+f118-f11a,u+f11c-f11d,u+f133,u+f147,u+f14e,u+f150-f152,u+f185-f186,u+f18e,u+f190-f192,u+f196,u+f1c1-f1c9,u+f1d9,u+f1db,u+f1e3,u+f1ea,u+f1f7,u+f1f9,u+f20a,u+f247-f248,u+f24a,u+f24d,u+f255-f25b,u+f25d,u+f271-f274,u+f278,u+f27b,u+f28c,u+f28e,u+f29c,u+f2b5,u+f2b7,u+f2ba,u+f2bc,u+f2be,u+f2c0-f2c1,u+f2c3,u+f2d0,u+f2d2,u+f2d4,u+f2dc}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-v4compatibility.woff2) format("woff2"),url(../webfonts/fa-v4compatibility.ttf) format("truetype");unicode-range:u+f041,u+f047,u+f065-f066,u+f07d-f07e,u+f080,u+f08b,u+f08e,u+f090,u+f09a,u+f0ac,u+f0ae,u+f0b2,u+f0d0,u+f0d6,u+f0e4,u+f0ec,u+f10a-f10b,u+f123,u+f13e,u+f148-f149,u+f14c,u+f156,u+f15e,u+f160-f161,u+f163,u+f175-f178,u+f195,u+f1f8,u+f219,u+f27a} -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TIC Cheatsheets", 3 | "short_name": "TIC Cheatsheets", 4 | "description": "Una colección de cheatsheets de distintos lenguajes de programación y herramientas, principalmente pensada para alumnos de TIC ORT.", 5 | "start_url": "/", 6 | "display": "standalone", 7 | "background_color": "#0c043f", 8 | "theme_color": "#0c043f", 9 | "orientation": "portrait-primary", 10 | "icons": [ 11 | { 12 | "src": "/assets/icons/maskable.png", 13 | "sizes": "192x192", 14 | "type": "image/png", 15 | "purpose": "maskable" 16 | }, 17 | { 18 | "src": "assets/icons/icon-72x72.png", 19 | "sizes": "72x72", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "assets/icons/icon-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image/png" 26 | }, 27 | { 28 | "src": "assets/icons/icon-128x128.png", 29 | "sizes": "128x128", 30 | "type": "image/png" 31 | }, 32 | { 33 | "src": "assets/icons/icon-144x144.png", 34 | "sizes": "144x144", 35 | "type": "image/png" 36 | }, 37 | { 38 | "src": "assets/icons/icon-152x152.png", 39 | "sizes": "152x152", 40 | "type": "image/png" 41 | }, 42 | { 43 | "src": "assets/icons/icon-192x192.png", 44 | "sizes": "192x192", 45 | "type": "image/png" 46 | }, 47 | { 48 | "src": "assets/icons/icon-384x384.png", 49 | "sizes": "384x384", 50 | "type": "image/png" 51 | }, 52 | { 53 | "src": "assets/icons/icon-512x512.png", 54 | "sizes": "512x512", 55 | "type": "image/png" 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /public/assets/fontawesome/css/v4-font-face.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 6.1.2 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | * Copyright 2022 Fonticons, Inc. 5 | */ 6 | @font-face { 7 | font-family: "FontAwesome"; 8 | font-display: block; 9 | src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); } 10 | 11 | @font-face { 12 | font-family: "FontAwesome"; 13 | font-display: block; 14 | src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); } 15 | 16 | @font-face { 17 | font-family: "FontAwesome"; 18 | font-display: block; 19 | src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); 20 | unicode-range: U+F003,U+F006,U+F014,U+F016-F017,U+F01A-F01B,U+F01D,U+F022,U+F03E,U+F044,U+F046,U+F05C-F05D,U+F06E,U+F070,U+F087-F088,U+F08A,U+F094,U+F096-F097,U+F09D,U+F0A0,U+F0A2,U+F0A4-F0A7,U+F0C5,U+F0C7,U+F0E5-F0E6,U+F0EB,U+F0F6-F0F8,U+F10C,U+F114-F115,U+F118-F11A,U+F11C-F11D,U+F133,U+F147,U+F14E,U+F150-F152,U+F185-F186,U+F18E,U+F190-F192,U+F196,U+F1C1-F1C9,U+F1D9,U+F1DB,U+F1E3,U+F1EA,U+F1F7,U+F1F9,U+F20A,U+F247-F248,U+F24A,U+F24D,U+F255-F25B,U+F25D,U+F271-F274,U+F278,U+F27B,U+F28C,U+F28E,U+F29C,U+F2B5,U+F2B7,U+F2BA,U+F2BC,U+F2BE,U+F2C0-F2C1,U+F2C3,U+F2D0,U+F2D2,U+F2D4,U+F2DC; } 21 | 22 | @font-face { 23 | font-family: "FontAwesome"; 24 | font-display: block; 25 | src: url("../webfonts/fa-v4compatibility.woff2") format("woff2"), url("../webfonts/fa-v4compatibility.ttf") format("truetype"); 26 | unicode-range: U+F041,U+F047,U+F065-F066,U+F07D-F07E,U+F080,U+F08B,U+F08E,U+F090,U+F09A,U+F0AC,U+F0AE,U+F0B2,U+F0D0,U+F0D6,U+F0E4,U+F0EC,U+F10A-F10B,U+F123,U+F13E,U+F148-F149,U+F14C,U+F156,U+F15E,U+F160-F161,U+F163,U+F175-F178,U+F195,U+F1F8,U+F219,U+F27A; } 27 | -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import type { AppProps } from "next/app"; 2 | import Head from "next/head"; 3 | import "../styles/globals.scss"; 4 | import Header from "../components/layout/Header"; 5 | import { useState } from "react"; 6 | import { ThemeProvider } from "../contexts/ThemeContext"; 7 | import Loading from "../components/utils/Loading"; 8 | import { LoadingProvider } from "../contexts/LoadingContext"; 9 | import Background from "../components/layout/Background"; 10 | import Script from "next/script"; 11 | 12 | function App({ Component, pageProps }: AppProps) { 13 | const [theme] = useState(true); 14 | 15 | return ( 16 | 17 | 18 | 19 | 23 | 24 | 39 |
40 |
41 | 42 | 43 |
44 | 45 |
46 |
47 | ); 48 | } 49 | 50 | export default App; 51 | -------------------------------------------------------------------------------- /public/assets/images/python.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /public/assets/fontawesome/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | // mixins 2 | // -------------------------- 3 | 4 | // base rendering for an icon 5 | @mixin fa-icon { 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | display: inline-block; 9 | font-style: normal; 10 | font-variant: normal; 11 | font-weight: normal; 12 | line-height: 1; 13 | } 14 | 15 | // sets relative font-sizing and alignment (in _sizing) 16 | @mixin fa-size ($font-size) { 17 | font-size: fa-divide($font-size, $fa-size-scale-base) * 1em; // converts step in sizing scale into an em-based value that's relative to the scale's base 18 | line-height: fa-divide(1, $font-size) * 1em; // sets the line-height of the icon back to that of it's parent 19 | vertical-align: (fa-divide(6, $font-size) - fa-divide(3, 8)) * 1em; // vertically centers the icon taking into account the surrounding text's descender 20 | } 21 | 22 | // only display content to screen readers 23 | // see: https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/ 24 | // see: https://hugogiraudel.com/2016/10/13/css-hide-and-seek/ 25 | @mixin fa-sr-only() { 26 | position: absolute; 27 | width: 1px; 28 | height: 1px; 29 | padding: 0; 30 | margin: -1px; 31 | overflow: hidden; 32 | clip: rect(0, 0, 0, 0); 33 | white-space: nowrap; 34 | border-width: 0; 35 | } 36 | 37 | // use in conjunction with .sr-only to only display content when it's focused 38 | @mixin fa-sr-only-focusable() { 39 | &:not(:focus) { 40 | @include fa-sr-only(); 41 | } 42 | } 43 | 44 | // convenience mixins for declaring pseudo-elements by CSS variable, 45 | // including all style-specific font properties, and both the ::before 46 | // and ::after elements in the duotone case. 47 | @mixin fa-icon-solid($fa-var) { 48 | @extend %fa-icon; 49 | @extend .fa-solid; 50 | 51 | &::before { 52 | content: unquote("\"#{ $fa-var }\""); 53 | } 54 | } 55 | 56 | @mixin fa-icon-regular($fa-var) { 57 | @extend %fa-icon; 58 | @extend .fa-regular; 59 | 60 | &::before { 61 | content: unquote("\"#{ $fa-var }\""); 62 | } 63 | } 64 | 65 | @mixin fa-icon-brands($fa-var) { 66 | @extend %fa-icon; 67 | @extend .fa-brands; 68 | 69 | &::before { 70 | content: unquote("\"#{ $fa-var }\""); 71 | } 72 | } 73 | 74 | -------------------------------------------------------------------------------- /public/assets/fontawesome/scss/_functions.scss: -------------------------------------------------------------------------------- 1 | // functions 2 | // -------------------------- 3 | 4 | // fa-content: convenience function used to set content property 5 | @function fa-content($fa-var) { 6 | @return unquote("\"#{ $fa-var }\""); 7 | } 8 | 9 | // fa-divide: Originally obtained from the Bootstrap https://github.com/twbs/bootstrap 10 | // 11 | // Licensed under: The MIT License (MIT) 12 | // 13 | // Copyright (c) 2011-2021 Twitter, Inc. 14 | // Copyright (c) 2011-2021 The Bootstrap Authors 15 | // 16 | // Permission is hereby granted, free of charge, to any person obtaining a copy 17 | // of this software and associated documentation files (the "Software"), to deal 18 | // in the Software without restriction, including without limitation the rights 19 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 20 | // copies of the Software, and to permit persons to whom the Software is 21 | // furnished to do so, subject to the following conditions: 22 | // 23 | // The above copyright notice and this permission notice shall be included in 24 | // all copies or substantial portions of the Software. 25 | // 26 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 31 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 32 | // THE SOFTWARE. 33 | 34 | @function fa-divide($dividend, $divisor, $precision: 10) { 35 | $sign: if($dividend > 0 and $divisor > 0, 1, -1); 36 | $dividend: abs($dividend); 37 | $divisor: abs($divisor); 38 | $quotient: 0; 39 | $remainder: $dividend; 40 | @if $dividend == 0 { 41 | @return 0; 42 | } 43 | @if $divisor == 0 { 44 | @error "Cannot divide by 0"; 45 | } 46 | @if $divisor == 1 { 47 | @return $dividend; 48 | } 49 | @while $remainder >= $divisor { 50 | $quotient: $quotient + 1; 51 | $remainder: $remainder - $divisor; 52 | } 53 | @if $remainder > 0 and $precision > 0 { 54 | $remainder: fa-divide($remainder * 10, $divisor, $precision - 1) * .1; 55 | } 56 | @return ($quotient + $remainder) * $sign; 57 | } 58 | -------------------------------------------------------------------------------- /public/assets/images/node.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/images/express.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /highlighters/prisma.js: -------------------------------------------------------------------------------- 1 | /* 2 | Language: Prisma 3 | Author: Bauty Garcia 4 | Description: Language definition for Prisma schema files (.prisma) 5 | */ 6 | 7 | function prisma(hljs) { 8 | 9 | //Incluye tipos de variables, como String, Int, CustomType, etc. y palabras que siguen a palabras clave como datasource, generator, model, enum, etc. 10 | const PRISMA_TYPE = { 11 | className: 'prisma-type', 12 | begin: /(? void; 6 | errorMsg: string; 7 | addCommand: () => void; 8 | changeCommand: (newCommand: string, i: number) => void; 9 | repeatCommand: (i: number) => string; 10 | clearCommands: () => void; 11 | commands: string[]; 12 | }; 13 | 14 | export const TerminalContext = createContext( 15 | null as null | TerminalContextType 16 | ); 17 | 18 | export const TerminalProvider = ({ 19 | children, 20 | }: { 21 | children: React.ReactNode; 22 | }) => { 23 | const [commands, setCommands] = useState([""]); 24 | const [error, setError] = useState(false); 25 | const [errorMsg, setErrorMsg] = useState(""); 26 | const addCommand = () => { 27 | setCommands([...commands, ""]); 28 | setErrorMsg(""); 29 | }; 30 | 31 | const clearCommands = () => { 32 | setCommands([]); 33 | setTimeout(() => { 34 | setCommands([""]); 35 | }, 100); 36 | }; 37 | const repeatCommand = (i: number) => { 38 | return commands[i]; 39 | }; 40 | 41 | useEffect(() => { 42 | let timeout: NodeJS.Timeout; 43 | if (error) { 44 | timeout = setTimeout(() => { 45 | setError(false); 46 | }, 500); 47 | } 48 | 49 | return () => { 50 | clearTimeout(timeout); 51 | }; 52 | }, [error]); 53 | 54 | const changeCommand = (newCommand: string, i: number) => { 55 | const newCommands = [...commands]; 56 | newCommands[i] = newCommand; 57 | setCommands(newCommands); 58 | }; 59 | 60 | const triggerError = () => { 61 | setError(true); 62 | setErrorMsg( 63 | "Comando no existente. Escriba 'help' para ver los comandos disponibles." 64 | ); 65 | }; 66 | 67 | return ( 68 | <> 69 | 81 | {children} 82 | 83 | 84 | ); 85 | }; 86 | -------------------------------------------------------------------------------- /components/layout/Header.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import ThemeSwitch from "../utils/ThemeSwitch"; 3 | import { useRouter } from "next/router"; 4 | import { twMerge } from "tailwind-merge"; 5 | import Image from "next/image"; 6 | import { useWindowSize } from "usehooks-ts"; 7 | 8 | const Header = () => { 9 | const { pathname } = useRouter(); 10 | const windowSize = useWindowSize(); 11 | 12 | return ( 13 |
14 | 15 | 16 | = 1250 20 | ? 45 21 | : windowSize.width >= 800 22 | ? 35 23 | : 30 24 | } 25 | width={ 26 | windowSize.width >= 1250 27 | ? 45 28 | : windowSize.width >= 800 29 | ? 35 30 | : 30 31 | } 32 | alt="Logo" 33 | /> 34 |

35 | {""} 36 |

37 |
38 | 39 | 52 |
53 | ); 54 | }; 55 | 56 | export default Header; 57 | -------------------------------------------------------------------------------- /components/utils/ThemeSwitch.tsx: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | 3 | import MoonIcon from "../../public/assets/icons/Moon.svg"; 4 | import SunIcon from "../../public/assets/icons/Sun.svg"; 5 | import { ThemeContext } from "../../contexts/ThemeContext"; 6 | 7 | import { twMerge } from "tailwind-merge"; 8 | 9 | const ThemeSwitch = () => { 10 | const { theme, setTheme } = useContext(ThemeContext)!; 11 | 12 | return ( 13 |
{ 16 | setTheme((theme) => (theme === "light" ? "dark" : "light")); 17 | }} 18 | > 19 |
25 | {/*
31 |
*/} 37 |
38 |
44 | {theme === "dark" ? ( 45 | 51 | ) : ( 52 | 58 | )} 59 |
60 |
61 | ); 62 | }; 63 | 64 | export default ThemeSwitch; 65 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./pages/**/*.{js,ts,jsx,tsx}", 5 | "./components/**/*.{js,ts,jsx,tsx}", 6 | "./styles/**/*.{js,ts,jsx,tsx,scss,css}", 7 | ], 8 | theme: { 9 | screens: { 10 | sm: "500px", 11 | md: "800px", 12 | lg: "1000px", 13 | xl: "1250px", 14 | "2xl": "1450px", 15 | "3xl": "1900px", 16 | }, 17 | fontFamily: { 18 | mono: ["Fira Code", "monospace"], 19 | space: ["Space Mono", "monospace"], 20 | raleway: ["Raleway", "sans-serif"], 21 | }, 22 | extend: { 23 | borderWidth: { 24 | 1: "1px", 25 | 3: "3px", 26 | }, 27 | borderRadius: { 28 | default: "15px", 29 | }, 30 | colors: { 31 | accent: "#e61366", 32 | "accent-hover": "#be1559", 33 | "accent-active": "#a20f4e", 34 | doc: "#e2e2e2", 35 | "dark-doc": "#170c60", 36 | "doc-link": "#3c3cff", 37 | "dark-doc-link": "#e61366", 38 | "doc-font": "#282828", 39 | "dark-doc-font": "#e2e2e2", 40 | }, 41 | boxShadow: { 42 | default: "0px 0px 15px 0px rgba(0, 0, 0, 0.3)", 43 | }, 44 | dropShadow: { 45 | default: "0px 0px 10px rgba(0, 0, 0, 0.3)", 46 | notebook: "2px 2px 10px rgba(0, 0, 0, 0.8)", 47 | }, 48 | backgroundImage: { 49 | main: "url('/assets/images/bg.svg')", 50 | github: "url('/assets/icons/GitHubLogo.png')", 51 | }, 52 | backgroundSize: { 53 | 200: "200%", 54 | }, 55 | keyframes: { 56 | shake: { 57 | "0%": { 58 | transform: "translateX(0)", 59 | }, 60 | "50%": { 61 | transform: "translateX(10px)", 62 | }, 63 | "100%": { 64 | transform: "translateX(0)", 65 | }, 66 | }, 67 | }, 68 | animation: { 69 | shake: "shake 0.2s ease-in-out infinite", 70 | }, 71 | }, 72 | }, 73 | plugins: [], 74 | }; 75 | -------------------------------------------------------------------------------- /public/assets/images/git.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /cheatsheets/node.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Node.js 3 | image: "/assets/images/node.svg" 4 | --- 5 | 6 | ## Índice 7 | 8 | - [Índice](#índice) 9 | - [Inicializar un proyecto](#inicializar-un-proyecto) 10 | - [Instalar dependencias](#instalar-dependencias) 11 | - [Carpeta `node_modules`](#carpeta-node_modules) 12 | - [Instalar dependencias de desarrollo](#instalar-dependencias-de-desarrollo) 13 | - [Instalar dependencias de forma global](#instalar-dependencias-de-forma-global) 14 | - [Sección `scripts`](#sección-scripts) 15 | 16 |
17 | 18 | ## Inicializar un proyecto 19 | 20 | Para inicializar un projecto deberemos ejecutar el siguiente comando: 21 | 22 | ```bash 23 | npm init 24 | ``` 25 | 26 | Este comando nos creará un archivo `package.json` con la información del proyecto, como el nombre, la versión, la descripción, etc. 27 | 28 | ## Instalar dependencias 29 | 30 | Para instalar dependencias deberemos ejecutar el siguiente comando: 31 | 32 | ```bash 33 | npm install 34 | ``` 35 | 36 | Si corremos este comando sin el nombre de ninguna dependencia, nos instalará todas las dependencias que tengamos en el archivo `package.json` en la sección `dependencies`. 37 | 38 | ### Carpeta `node_modules` 39 | 40 | La carpeta `node_modules` es donde se instalan todas las dependencias de nuestro proyecto. Esta carpeta no se sube al repositorio, ya que es muy pesada y no es necesario que esté en el mismo. Para evitar esto, deberemos crear un archivo `.gitignore` en la raíz del proyecto y añadir la siguiente línea: 41 | 42 | ```bash 43 | node_modules/ 44 | ``` 45 | 46 | ## Instalar dependencias de desarrollo 47 | 48 | Para instalar dependencias de desarrollo deberemos ejecutar el siguiente comando: 49 | 50 | ```bash 51 | npm install --save-dev 52 | ``` 53 | 54 | ## Instalar dependencias de forma global 55 | 56 | Para instalar dependencias de forma global deberemos ejecutar el siguiente comando: 57 | 58 | ```bash 59 | npm install -g 60 | ``` 61 | 62 | ## Sección `scripts` 63 | 64 | Esta sección del archivo `package.json` es donde podemos añadir comandos que queramos ejecutar desde la terminal. Por ejemplo, si queremos utilizar `nodemon` para que se reinicie el servidor cada vez que guardemos un archivo, podemos añadir el siguiente comando: 65 | 66 | ```json 67 | { 68 | "name": "node", 69 | "version": "1.0.0", 70 | "description": "", 71 | "main": "index.js", 72 | "scripts": { 73 | "dev": "npx nodemon index.js" 74 | }, 75 | "author": "", 76 | "license": "ISC" 77 | } 78 | ``` 79 | 80 | Y luego ejecutarlo con el siguiente comando: 81 | 82 | ```bash 83 | npm run dev 84 | ``` 85 | -------------------------------------------------------------------------------- /public/assets/images/nextjs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/download/socket.js: -------------------------------------------------------------------------------- 1 | class SoqueticError extends Error { 2 | constructor(message) { 3 | super(message); 4 | this.name = "SoqueticError"; 5 | } 6 | } 7 | 8 | const socket = io("http://localhost:3000", { 9 | autoConnect: false, 10 | }); 11 | 12 | socket.on("connect", () => { 13 | console.log("¡Conectado al backend!"); 14 | }); 15 | 16 | socket.on("connect_error", () => { 17 | throw new SoqueticError( 18 | "Error al conectar al backend. Revisá que el servidor no haya crasheado y esté corriendo en el puerto correcto.\nRecargá la página para reconectar." 19 | ); 20 | }); 21 | 22 | const assertConnection = (socket) => { 23 | if (!socket.active) { 24 | throw new SoqueticError( 25 | "No se puede enviar un evento si no hay conexión al backend.\nRecordá que tenés que llamar a connect2Server() para conectarte al backend." 26 | ); 27 | } 28 | }; 29 | 30 | const assertTypeIsString = (type) => { 31 | if (typeof type !== "string") { 32 | throw new SoqueticError( 33 | `El tipo de evento debe ser un string, pero es de tipo ${typeof type}` 34 | ); 35 | } 36 | }; 37 | 38 | const assertCallbackIsFunction = (callback) => { 39 | if (typeof callback !== "function") { 40 | throw new SoqueticError( 41 | `El callback debe ser una función, pero es de tipo ${typeof callback}` 42 | ); 43 | } 44 | }; 45 | 46 | const RESTCallbackDecorator = (callback) => { 47 | assertCallbackIsFunction(callback); 48 | return (response) => { 49 | if (response.status !== 200) { 50 | throw new SoqueticError( 51 | response.message ? response.message : "Error desconocido" 52 | ); 53 | } 54 | callback(response.data); 55 | }; 56 | }; 57 | 58 | const send = (type, data, callback = () => {}) => { 59 | assertConnection(socket); 60 | assertTypeIsString(type); 61 | socket.emit("realTimeEvent", type, data, RESTCallbackDecorator(callback)); 62 | }; 63 | 64 | const receive = (type, callback) => { 65 | assertConnection(socket); 66 | assertTypeIsString(type); 67 | socket.on("realTimeEvent", (receivedType, data) => { 68 | if (receivedType === type) return callback(data); 69 | }); 70 | }; 71 | 72 | const fetchData = (type, callback) => { 73 | assertConnection(socket); 74 | assertTypeIsString(type); 75 | socket.emit("GETEvent", type, RESTCallbackDecorator(callback)); 76 | }; 77 | 78 | const postData = (type, data, callback = () => {}) => { 79 | assertConnection(socket); 80 | assertTypeIsString(type); 81 | socket.emit("POSTEvent", type, data, RESTCallbackDecorator(callback)); 82 | }; 83 | 84 | const connect2Server = (PORT = 3000) => { 85 | socket.io.uri = `http://localhost:${PORT}`; 86 | socket.connect(); 87 | }; 88 | -------------------------------------------------------------------------------- /cheatsheets/mysql2.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: mysql2 3 | image: "/assets/images/mysql2.svg" 4 | --- 5 | 6 | ## Índice 7 | 8 | - [Índice](#índice) 9 | - [Introducción](#introducción) 10 | - [Instalación](#instalación) 11 | - [Conexión](#conexión) 12 | - [Consultas (Queries)](#consultas-queries) 13 | - [Consultas sin parámetros](#consultas-sin-parámetros) 14 | - [Consultas con parámetros](#consultas-con-parámetros) 15 | 16 |
17 | 18 | ## Introducción 19 | 20 | `mysql2` es una librería de Node.js que permite realizar una conexión a una base de datos. En este documento utilizaremos en específico la parte de la librería que utiliza promesas para manejar la asincronía (`mysql2/promise`). 21 | 22 | ## Instalación 23 | 24 | Para instalar `mysql2` deberemos ejecutar el siguiente comando (dentro de un proyecto de Node.js): 25 | 26 | ```bash 27 | npm install mysql2 28 | ``` 29 | 30 | ## Conexión 31 | 32 | Para conectarnos a la base de datos deberemos añadir el siguiente código: 33 | 34 | ```js 35 | import mysql from "mysql2/promise"; 36 | 37 | const connection = await mysql.createConnection({ 38 | host: "", 39 | user: "", 40 | password: "", 41 | database: "", 42 | }); 43 | ``` 44 | 45 | En `connection` tendremos un objeto que nos permitirá hacer consultas a la base de datos. 46 | 47 | **Aclaración:** (recordar que para poder importar las librerías con `import` y para poder utilizar `await` fuera de una función deben utilizar `"type": "module"` en el `package.json`) 48 | 49 | ## Consultas (Queries) 50 | 51 | ### Consultas sin parámetros 52 | 53 | Para hacer consultas a la base de datos deberemos añadir el siguiente código: 54 | 55 | ```js 56 | const [result, fields] = await connection.query("ACÁ VA LA CONSULTA SQL"); 57 | ``` 58 | 59 | En el caso de que la consulta sea un `SELECT`, en `result` tendremos un array con los resultados de la consulta. En caso de que sea un `INSERT`, `UPDATE` o `DELETE`, en `result` tendremos un objeto con información sobre la consulta. 60 | 61 | En `fields` tendremos información sobre las columnas de la tabla, como el nombre, el tipo, etc. 62 | 63 | ### Consultas con parámetros 64 | 65 | Para hacer consultas a la base de datos con parámetros deberemos añadir el siguiente código: 66 | 67 | ```js 68 | const [result, fields] = await connection.query( 69 | "ACÁ VA LA CONSULTA SQL CON ? Y ?", 70 | [parametro1, parametro2] 71 | ); 72 | ``` 73 | 74 | Este código funciona de la misma manera que el anterior, pero en este caso, los `?` serán reemplazados por los parámetros que pasemos en el array. Es decir, la query final que se ejecutará será `"ACÁ VA LA CONSULTA SQL CON parametro1 Y parametro2"`. 75 | 76 | Esto es útil para evitar inyecciones SQL, haciendo mucho más seguro nuestro código. 77 | 78 | **Aclaración:** Los parámetros deben ser pasados en el mismo orden que los `?` en la consulta SQL. Porque el primer `?` será reemplazado por el primer elemento del array, el segundo `?` por el segundo elemento, y así sucesivamente. 79 | -------------------------------------------------------------------------------- /components/utils/Terminal/Autocomplete.tsx: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect, useReducer, useState } from "react"; 2 | import { TerminalContext } from "../../../contexts/TerminalContext"; 3 | import { twMerge } from "tailwind-merge"; 4 | 5 | export type AutocompleteType = { 6 | hidden: string; 7 | visible: string; 8 | }; 9 | 10 | type AutocompleteComponentType = { 11 | content: string; 12 | list: string[]; 13 | padding?: string; 14 | }; 15 | 16 | const autocompleteReducer = ( 17 | state: AutocompleteType, 18 | action: { 19 | type: "SET" | "CLEAR"; 20 | payload: { content: string; length: number }; 21 | } 22 | ) => { 23 | switch (action.type) { 24 | case "SET": 25 | const { content, length } = action.payload; 26 | const hidden = content.slice(0, length); 27 | const visible = content.slice(length); 28 | return { hidden, visible }; 29 | case "CLEAR": 30 | return { hidden: "", visible: "" }; 31 | default: 32 | return state; 33 | } 34 | }; 35 | 36 | export const AutocompleteCommand = ({ 37 | content, 38 | list, 39 | padding, 40 | }: AutocompleteComponentType) => { 41 | const [autocompleteValue, setAutocompleteValue] = useReducer( 42 | autocompleteReducer, 43 | { 44 | hidden: "", 45 | visible: "", 46 | } 47 | ); 48 | 49 | const { error } = useContext(TerminalContext); 50 | 51 | useEffect(() => { 52 | if (content === "") 53 | return setAutocompleteValue({ 54 | type: "CLEAR", 55 | payload: { content: "", length: 0 }, 56 | }); 57 | 58 | const command = list.find((c) => c.startsWith(content)); 59 | 60 | if (command) { 61 | setAutocompleteValue({ 62 | type: "SET", 63 | payload: { content: command, length: content.length }, 64 | }); 65 | } else { 66 | setAutocompleteValue({ 67 | type: "CLEAR", 68 | payload: { content: "", length: 0 }, 69 | }); 70 | } 71 | }, [content]); 72 | 73 | return ( 74 | <> 75 | 76 | {padding} 77 | {autocompleteValue.hidden} 78 | 79 | 82 | {autocompleteValue.visible} 83 | 84 | 85 | ); 86 | }; 87 | 88 | export const AutocompleteFile = ({ 89 | list, 90 | content, 91 | }: { 92 | list: string[]; 93 | content: string; 94 | }) => { 95 | const [fileName, setFileName] = useState(""); 96 | const [padding, setPadding] = useState(""); 97 | 98 | useEffect(() => { 99 | const command = content.split(" "); 100 | // console.log(file); 101 | setFileName(command[1] || ""); 102 | setPadding(command[0] + " "); 103 | }, [content]); 104 | 105 | return ( 106 | 107 | ); 108 | }; 109 | -------------------------------------------------------------------------------- /public/assets/images/bash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /cheatsheets/bash.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Terminal (bash) 3 | image: "/assets/images/bash.svg" 4 | --- 5 | 6 | ## Índice 7 | 8 | - [Índice](#índice) 9 | - [¿Qué terminal usar?](#qué-terminal-usar) 10 | - [Sistema de archivos](#sistema-de-archivos) 11 | - [Rutas absolutas](#rutas-absolutas) 12 | - [Rutas relativas](#rutas-relativas) 13 | - [Comandos básicos](#comandos-básicos) 14 | - [Comandos de navegación en directorios](#comandos-de-navegación-en-directorios) 15 | - [Comandos de programas](#comandos-de-programas) 16 | 17 |
18 | 19 | ## ¿Qué terminal usar? 20 | 21 | Para utilizar los comandos que aparecerán en este documento, no será posible utilizar la terminal de Windows (cmd). Para ello, se recomienda utilizar la terminal Windows PowerShell o la terminal de Linux (Ubuntu). 22 | 23 | ## Sistema de archivos 24 | 25 | ### Rutas absolutas 26 | 27 | Las rutas absolutas son una forma de ubicar un archivo en una computadora/servidor de manera global, es decir, desde cualquier ubicación arbitraria, es posible acceder al archivo especificado por medio de estas. Las utilizamos todos los días por ejemplo en el explorador de archivos, todos tenemos los archivos de los distintos programas en ‘C:\Program Files’ la cual es una ruta absoluta ya que empieza con el disco (`C:\`). 28 | 29 | ### Rutas relativas 30 | 31 | Las rutas relativas, en cambio, son una forma de localizar un archivo desde la ubicación actual en la que se encuentra el usuario. Podemos pensarlo como las direcciones que le damos a alguien que ya está ubicado en una ruta, para llegar al lugar que queremos. Para esta tarea, utilizaremos los siguientes comandos: 32 | 33 | **‘./’**: Este comando representa la carpeta actual del archivo en cuestión. 34 | **‘../’**: Este comando representa la carpeta padre de la carpeta actual. 35 | 36 | **Ejemplo:** 37 | Si el árbol de carpetas fuera el siguiente: 38 | 39 | Si nosotros estamos en el archivo `index.php`, entonces para acceder a la carpeta test debemos utilizar la ruta relativa `../test/`, la cual significa subir una carpeta en el árbol y luego entrar a la carpeta `test`. 40 | 41 | ## Comandos básicos 42 | 43 | ### Comandos de navegación en directorios 44 | 45 | | Comando | Uso | Descripción | 46 | | ------- | --------------------- | ----------------------------- | 47 | | `cd` | `cd carpeta_proyecto` | Cambia de directorio | 48 | | `cd` | `cd ..` | Vuelve al directorio anterior | 49 | | `ls` | `ls` | Lista los archivos | 50 | | `pwd` | `pwd` | Muestra la ruta actual | 51 | 52 | ### Comandos de programas 53 | 54 | | Comando | Uso | Descripción | 55 | | ---------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------- | 56 | | `explorer` | `explorer .` | Abre el explorador de archivos en la carpeta actual | 57 | | `code` | `code .` | Abre Visual Studio Code en la carpeta actual | 58 | | `git` | `git status` | Muestra el estado del repositorio ([para más comandos](https://cheatsheets-nachovigilante.vercel.app/cheatsheet/git)) | 59 | | `npm` | `npm start` | Ejecuta el comando `start` de `package.json` ([para más comandos](https://cheatsheets-nachovigilante.vercel.app/cheatsheet/node)) | 60 | -------------------------------------------------------------------------------- /public/assets/images/cloudinary.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cheatsheets de TIC 2 | 3 | Este es el repositorio de la web de cheatsheets de TIC. Para acceder a la web [hacer click acá](https://cheatsheets-nv.vercel.app/). 4 | 5 | ## ¿Cómo hago un cambio? 6 | 7 | ### ¿Mi cambio tiene que ser fundamental? 8 | 9 | No. Todos los cambios son importantes, incluso si el cambio únicamente incluye agregar una tilde en este mismo documento. Cada uno puede aportar su parte para mejorar la calidad de este (y otros...) repositorios. De eso se trata la filosofía [Open Source](https://opensource.org/about), no es necesario saber programar mejor que otro, o entender más de un lenguaje u otra herramienta, todos cometemos errores y cualquiera puede ser capaz de corregirlos. Incluso si el cambio no es significativo, es una buena forma de practicar el proceso que conlleva realizar una [pull request](https://docs.github.com/es/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) (PR). 10 | 11 | ### Pasos a seguir 12 | 13 | Para proponer algún cambio que agregue información o mejore algo en el repo hay que seguir los siguientes pasos: 14 | 15 | - Hacer un fork al repo 16 | - Realizar algún cambio detallando en el/los commits (utilizando [conventional commits](https://www.conventionalcommits.org/)) lo realizado 17 | - [Hacer un PR](#ejemplo-de-pr) con el/los commits realizados 18 | - Esperar la respuesta al PR 19 | 20 | ### Convención para el commit message 21 | 22 | Para los commits, vamos a utilizar una convención llamada [conventional commits](https://www.conventionalcommits.org/). El proyecto está configurado para que no se pueda committear si no se sigue esta convención. 23 | 24 | **Importante si usas linux o mac:** correr `npm run unix` una vez o no van a poder commitear. 25 | 26 | ### Ejemplo de PR 27 | 28 | En esta sección podemos ver un [ejemplo de un PR](https://github.com/nachovigilante/cheatsheets/pull/1) que realizó [@Sponja](https://github.com/Sponja-) para arreglar un problema en el índice del cheatsheet de PHP. 29 | 30 | ![Ejemplo de PR](/public/assets/images/PR.png) 31 | 32 | El PR puede recibir una respuesta positiva o negativa, si la respuesta es positiva el cambio se acepta y si es negativa se rechaza, lo que no significa que la corrección o el arreglo no es necesario, sino que tal vez es necesario mejorar la corrección antes de aceptarla. 33 | 34 | Una vez que el PR es aceptado, el cambio se aplica al repo y se generará un nuevo commit con el cambio, como el que podemos [ver en este commit](https://github.com/nachovigilante/cheatsheets/commit/1fc56153a09720a09a724b600c7386423c83cd66). 35 | 36 | ## Agregar nuevos cheatsheets 37 | 38 | Si se quiere agregar un nuevo cheatsheet de algún lenguaje o herramienta que no se encuentra en el repo, ésta debe ser agregada en formato de archivo `.md`, en la carpeta `/cheatsheets/` con el nombre del lenguaje o la herramienta en cuestión. Es importante, además de seguir los pasos en la [sección anterior](#cómo-hago-un-cambio), cumplir los requerimientos obligatorios. 39 | 40 | ### Requisitos 41 | 42 | - Agregar además del `.md` un logo correspondiente en la carpeta `/assets/images/` que debe estar en formato `svg` y debe tener el mismo nombre que el archivo `.md` (ejemplo: `php.md` y `php.svg`). Intentar que el logo tenga un tamaño adecuado de alrededor de 20x20px, para que se vea bien en la web. 43 | 44 | - Tener una sección de metadata que tenga la siguiente estructura: 45 | 46 | ```plaintext 47 | --- 48 | title: {título} 49 | --- 50 | ``` 51 | 52 | - La primer sección debe ser llamada "Índice" y debe contener una tabla de contenidos (`TOC`), con la misma estructura que la presente en los cheatsheets que se encuentran actualmente en la carpeta `/cheatsheets/`, seguida de un `
` por razones puramente estéticas 53 | 54 | ### Recomendaciones 55 | 56 | - Citar las fuentes de información para acelerar el proceso de verificación y validación 57 | - Hacer uso de las herramientas que brinda markdown para mejorar la legibilidad de la información, aligerando también el proceso de feedback 58 | -------------------------------------------------------------------------------- /public/assets/icons/Sun.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /public/assets/images/react.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pages/cheatsheet/[slug].tsx: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import matter from "gray-matter"; 3 | import path from "path"; 4 | import Head from "next/head"; 5 | import "highlight.js/styles/github-dark.css"; 6 | import React, { useContext, useEffect } from "react"; 7 | import FloatingButton from "../../components/utils/FloatingButton"; 8 | import { LoadingContext } from "../../contexts/LoadingContext"; 9 | import { ThemeContext } from "../../contexts/ThemeContext"; 10 | import router from "next/router"; 11 | import hljs from "highlight.js"; 12 | import marked from "marked-katex"; 13 | import katex from "katex"; 14 | import { twMerge } from "tailwind-merge"; 15 | import prisma from "../../highlighters/prisma"; 16 | 17 | hljs.registerLanguage("prisma", prisma); 18 | 19 | marked.setOptions({ 20 | highlight: function (code, lang) { 21 | if (typeof lang === "undefined") { 22 | return hljs.highlightAuto(code).value; 23 | } else if (lang === "nohighlight") { 24 | return code; 25 | } else { 26 | return hljs.highlight(lang, code).value; 27 | } 28 | }, 29 | kaTex: katex, 30 | }); 31 | 32 | type CheatsheetPageProps = { 33 | frontmatter: { 34 | title: string; 35 | image: string; 36 | }; 37 | content: string; 38 | slug: string; 39 | }; 40 | 41 | const CheatsheetPage = ({ 42 | frontmatter: { title, image }, 43 | content, 44 | slug, 45 | }: CheatsheetPageProps) => { 46 | const { setLoading } = useContext(LoadingContext); 47 | const { theme } = useContext(ThemeContext); 48 | 49 | useEffect(() => { 50 | router.events.on("routeChangeStart", () => setLoading(true)); 51 | router.events.on("routeChangeComplete", () => setLoading(false)); 52 | 53 | return () => { 54 | router.events.off("routeChangeStart", () => setLoading(true)); 55 | router.events.off("routeChangeComplete", () => setLoading(false)); 56 | }; 57 | }, []); 58 | 59 | // console.log(slug); 60 | 61 | return ( 62 | <> 63 | 64 | {title} 65 | 66 |
72 |

73 | {title} 74 |

75 |
84 |
85 |
86 | window.scrollTo(0, 0)} 88 | ariaLabel="Scroll to the top" 89 | > 90 | 91 | 92 |
93 | 94 | ); 95 | }; 96 | 97 | export const getStaticPaths = async () => { 98 | const files = fs.readdirSync(path.join("cheatsheets")); 99 | 100 | const paths = files 101 | .map((file) => { 102 | const slug = file.replace(".md", ""); 103 | 104 | return { 105 | params: { 106 | slug, 107 | }, 108 | }; 109 | }) 110 | .filter((p) => p.params.slug !== "index"); 111 | 112 | return { 113 | paths, 114 | fallback: false, 115 | }; 116 | }; 117 | 118 | export const getStaticProps = async ({ params: { slug } }) => { 119 | const markdownWithMeta = fs.readFileSync( 120 | path.join("cheatsheets", `${slug}.md`), 121 | "utf8" 122 | ); 123 | 124 | const { data: frontmatter, content } = matter(markdownWithMeta); 125 | 126 | return { 127 | props: { 128 | frontmatter, 129 | content, 130 | slug, 131 | } as CheatsheetPageProps, 132 | }; 133 | }; 134 | 135 | export default CheatsheetPage; 136 | -------------------------------------------------------------------------------- /public/assets/images/soquetic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /components/utils/Terminal/Terminal.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import { useContext, useRef } from "react"; 3 | import { twMerge } from "tailwind-merge"; 4 | import { 5 | TerminalContext, 6 | TerminalProvider, 7 | } from "../../../contexts/TerminalContext"; 8 | import { CheatsheetType } from "../../../pages"; 9 | import Command from "./Command"; 10 | import Image from "next/image"; 11 | import { useWindowSize } from "usehooks-ts"; 12 | 13 | type TerminalProps = { 14 | cheatsheets: CheatsheetType[]; 15 | }; 16 | 17 | const Console = ({ cheatsheets }: TerminalProps) => { 18 | const { commands } = useContext(TerminalContext); 19 | 20 | return ( 21 |
22 |
23 | {commands.map((c, i) => ( 24 | 31 | ))} 32 |
33 |
34 | ); 35 | }; 36 | 37 | const WindowHeader = () => { 38 | return ( 39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | tic://cheatsheets 47 |
48 |
49 | ); 50 | }; 51 | 52 | const Sidebar = ({ cheatsheets }: TerminalProps) => { 53 | const windowSize = useWindowSize(); 54 | return ( 55 |
88 | ); 89 | }; 90 | 91 | const Window = ({ cheatsheets }: TerminalProps) => { 92 | const { error } = useContext(TerminalContext); 93 | 94 | return ( 95 |
101 | 102 |
103 | 104 | 105 |
106 |
107 | ); 108 | }; 109 | 110 | const Terminal = ({ cheatsheets }: TerminalProps) => { 111 | return ( 112 | 113 | 114 | 115 | ); 116 | }; 117 | 118 | export default Terminal; 119 | -------------------------------------------------------------------------------- /cheatsheets/git.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Git-GitHub 3 | image: "/assets/images/git.svg" 4 | --- 5 | 6 | 7 | 8 | ## Índice 9 | 10 | - [Índice](#índice) 11 | - [¿Qué es?](#qué-es) 12 | - [Comandos básicos](#comandos-básicos) 13 | - [Crear un nuevo repositorio](#crear-un-nuevo-repositorio) 14 | - [Clonar un repositorio](#clonar-un-repositorio) 15 | - [Añadir un archivo](#añadir-un-archivo) 16 | - [Añadir todos los archivos](#añadir-todos-los-archivos) 17 | - [Hacer un commit](#hacer-un-commit) 18 | - [Hacer un push](#hacer-un-push) 19 | - [Hacer un pull](#hacer-un-pull) 20 | - [Ver el estado del repositorio](#ver-el-estado-del-repositorio) 21 | - [Ver el historial de commits](#ver-el-historial-de-commits) 22 | - [Branches](#branches) 23 | - [Nueva branch](#nueva-branch) 24 | - [Ver branches existentes](#ver-branches-existentes) 25 | - [Moverse entre branches](#moverse-entre-branches) 26 | - [Moverse a una branch remota](#moverse-a-una-branch-remota) 27 | - [Mergear branches](#mergear-branches) 28 | - [Eliminar una branch](#eliminar-una-branch) 29 | - [GitHub](#github) 30 | - [Crear un nuevo repositorio en GitHub](#crear-un-nuevo-repositorio-en-github) 31 | - [Clonar un repositorio de GitHub](#clonar-un-repositorio-de-github) 32 | - [Añadir un colaborador](#añadir-un-colaborador) 33 | 34 | 35 | ## ¿Qué es? 36 | 37 | Git es un sistema de control de versiones distribuido, es decir, que cada usuario tiene una copia completa del repositorio. Esto nos permite trabajar de forma local y sincronizar los cambios con el repositorio remoto cuando queramos. 38 | 39 | ## Comandos básicos 40 | 41 | ### Crear un nuevo repositorio 42 | 43 | ```bash 44 | git init 45 | ``` 46 | 47 | ### Clonar un repositorio 48 | 49 | ```bash 50 | git clone https://www.github.com/username/repo.git 51 | ``` 52 | 53 | ### Añadir un archivo 54 | 55 | ```bash 56 | git add file.txt 57 | ``` 58 | 59 | ### Añadir todos los archivos 60 | 61 | ```bash 62 | git add . 63 | ``` 64 | 65 | ### Hacer un commit 66 | 67 | ```bash 68 | git commit -m "Mensaje del commit" 69 | ``` 70 | 71 | Descripción completa 72 | 73 | ```bash 74 | git commit -m "Mensaje del commit" -m "Descripción del commit" 75 | ``` 76 | 77 | ### Hacer un push 78 | 79 | ```bash 80 | git push origin main 81 | ``` 82 | 83 | Puede también ser en repositorios más viejos que sea `git push origin master`. Master o Main son los nombres de la branch que están pusheando, y puede variar si están pusheando otra branch. 84 | 85 | ### Hacer un pull 86 | 87 | ```bash 88 | git pull origin main 89 | ``` 90 | 91 | Al igual que en push, en repositiorios más viejos puede ser `git pull origin master` 92 | 93 | ### Ver el estado del repositorio 94 | 95 | ```bash 96 | git status 97 | ``` 98 | 99 | ### Ver el historial de commits 100 | 101 | ```bash 102 | git log 103 | ``` 104 | 105 | ## Branches 106 | 107 | ### Nueva branch 108 | 109 | ```bash 110 | git switch -c 111 | ``` 112 | 113 | La opción -c es para crear la branch, de otra forma `switch` solo nos deja movernos entre branches existentes. Si se quiere crear una branch sin movernos, es `git branch `. 114 | 115 | ### Ver branches existentes 116 | 117 | ```bash 118 | git branch 119 | ``` 120 | 121 | ### Moverse entre branches 122 | 123 | ```bash 124 | git switch 125 | ``` 126 | 127 | ### Moverse a una branch remota 128 | 129 | ```bash 130 | git fetch 131 | git switch 132 | ``` 133 | 134 | Se hace `git fetch` antes del `switch` para que el repositorio local conozca que existe esa branch en el repositorio remoto. 135 | 136 | ### Mergear branches 137 | 138 | ```bash 139 | git merge 140 | ``` 141 | 142 | Se mergea sobre la branch actual la branch a mergear. **NO** elimina la branch a mergear. 143 | 144 | ### Eliminar una branch 145 | 146 | ```bash 147 | git branch -D 148 | ``` 149 | 150 | La opción `-D` es para eliminar la branch, de otra forma `branch` solo crea esa branch, que falla si la branch ya existía. 151 | 152 | ## GitHub 153 | 154 | GitHub es una plataforma de desarrollo colaborativo que permite alojar proyectos utilizando el sistema de control de versiones Git. 155 | 156 | ### Crear un nuevo repositorio en GitHub 157 | 158 | Para crear un nuevo repositorio deberemos ir a la página de [GitHub](https://github.com/) y hacer click en el botón `New repository`. 159 | 160 | Home 161 | Home Create Highlight 162 | New 163 | New Button 164 | New Highlight 165 | 166 | ### Clonar un repositorio de GitHub 167 | 168 | Para clonar un repositorio deberemos ir a la página del repositorio y hacer click en el botón `Clone or download`. 169 | 170 | ### Añadir un colaborador 171 | 172 | Para añadir un colaborador deberemos ir a la página del repositorio y hacer click en `Settings`. Una vez dentro, deberemos ir a la sección `Collaborators` y añadir el nombre de usuario del colaborador. 173 | -------------------------------------------------------------------------------- /public/assets/images/mysql2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/assets/images/ino.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /cheatsheets/postgres.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: postgres 3 | image: "/assets/images/mysql2.svg" 4 | --- 5 | 6 | ## Índice 7 | 8 | - [Índice](#índice) 9 | - [Introducción](#introducción) 10 | - [Creación de una base de datos en Vercel](#creación-de-una-base-de-datos-en-vercel) 11 | - [Conexión a la base de datos](#conexión-a-la-base-de-datos) 12 | - [pgAdmin](#pgadmin) 13 | - [Conexión a la base de datos desde pgAdmin](#conexión-a-la-base-de-datos-desde-pgadmin) 14 | - [Creación de una tabla](#creación-de-una-tabla) 15 | - [Tipos de datos](#tipos-de-datos) 16 | - [Autoincremental](#autoincremental) 17 | - [Claves foráneas](#claves-foráneas) 18 | - [pg en Node.js](#pg-en-nodejs) 19 | - [Instalación](#instalación) 20 | - [Utilización](#utilización) 21 | - [Conexión](#conexión) 22 | - [Consultas](#consultas) 23 | 24 |
25 | 26 | ## Introducción 27 | 28 | PostgreSQL es un motor de base de datos relacional SQL. Será el encargado de almacenar y gestionar los datos de nuestra aplicación. PostgreSQL es el sistema más común entre los sistemas que prestan servicio de hosting de bases de datos de manera gratuita en algunos planes. En particular, esta cheatsheet se enfoca en utilizar PostgreSQL, pgAdmin (una interfaz gráfica para PostgreSQL) y conectarlo a una base de datos hosteada en Vercel. 29 | 30 | ## Creación de una base de datos en Vercel 31 | 32 | Una vez que ingresaron a su cuenta de Vercel, y crearon un proyecto en el que desean utilizar una base de datos PostgreSQL, sigan los siguientes pasos para crear una base de datos en Vercel: 33 | 34 | Ir a la sección "Storage" del proyecto. 35 |
36 |
37 | ![storage](/assets/images/postgres/storage.png) 38 |
39 | 40 | Luego, tendrán que seleccionar para crear una nueva base de datos en PostgreSQL. 41 | 42 | ## Conexión a la base de datos 43 | 44 | Para conectarse a la base de datos, tanto desde pgAdmin como desde la aplicación, se necesitan datos que se encontrarán en la sección "Quickstart" de la base de datos creada en Vercel. 45 |
46 |
47 | ![connection](/assets/images/postgres/connection.png) 48 |
49 | 50 | ## pgAdmin 51 | 52 | pgAdmin es una interfaz gráfica para PostgreSQL que permite visualizar y manipular los datos de la base de datos. 53 | 54 | ### Conexión a la base de datos desde pgAdmin 55 | 56 | Para conectarse a la base de datos desde pgAdmin, sigan los siguientes pasos: 57 | 58 | 1. Descargar pgAdmin desde [pgAdmin](https://www.pgadmin.org/download/). 59 | 2. Abrir pgAdmin y agregar un nuevo servidor 60 | ![new_server](/assets/images/postgres/new_server.png) 61 | 3. Completar los datos del servidor con los datos de la base de datos de Vercel vistos en la sección "Conexión a la base de datos". 62 | ![server_data](/assets/images/postgres/server_data.png) 63 | 64 | Una vez que se conectaron a la base de datos, podrán visualizar y manipular los datos de las distintas tablas que tengan en la base de datos. 65 | 66 | Es importante remarcar que si la base de datos que utilizaremos es la llamada "verceldb", pues no tenemos permisos para editar la llamada "postgres". 67 | 68 | ### Creación de una tabla 69 | 70 | Para crear una tabla en la base de datos, deberán acceder desde el menú de la izquierda a "Servers" -> "nombre_del_servidor" -> "Databases" -> "verceldb" -> "Schemas" -> "public" -> "Tables" -> "Create" -> "Table...". 71 | 72 | Luego, deberán completar los datos de la tabla que desean crear. Y agregando las columnas que deseen. 73 | 74 | ![table](/assets/images/postgres/table.png) 75 | 76 | En general, queremos que todos los campos sean NOT NULL, excepto los que no sean necesarios. 77 | 78 | #### Tipos de datos 79 | 80 | - `integer`: Números enteros. 81 | - `real`: Números con coma. 82 | - `character varying`: Es el equivalente a `varchar` en otros motores de base de datos. 83 | - `text`: Texto. 84 | - `date`: Fecha. 85 | - `timestamp with time zone`: Fecha y hora. 86 | - `boolean`: Verdadero o falso. 87 | 88 | ##### Autoincremental 89 | 90 | Para que un campo sea autoincremental, deberán seleccionar la columna y darle el tipo de dato `serial`. Es importante que el campo que sea autoincremental sea la clave primaria de la tabla. 91 | 92 | ### Claves foráneas 93 | 94 | Para agregar una clave foránea, deberán dar botón derecho en la tabla, entrar en la opción "Properties". Luego, en la pesataña "Constraints" seleccionar seleccionar "Foreign Key". Finalmente, deberán completar los datos de la clave foránea (la columna que será foreign key, la clave que referencia y su respectiva tabla). 95 | 96 | ## pg en Node.js 97 | 98 | Para conectarse a la base de datos desde Node.js, se puede utilizar el paquete `pg`. Aquí su [documentación](https://node-postgres.com/). 99 | 100 | ### Instalación 101 | 102 | Para instalarlo, deberán correr el siguiente comando (dentro de un proyecto de Node.js): 103 | 104 | ```bash 105 | npm install pg 106 | ``` 107 | 108 | ### Utilización 109 | 110 | #### Conexión 111 | 112 | Una vez que instalaron el paquete, podrán conectarse a la base de datos y realizar consultas. 113 | 114 | ```javascript 115 | import { Client } from "pg"; // Importamos el cliente de pg (recordar que para utilizar 'import' es necesario usar "type": "module" en el package.json) 116 | 117 | // Pueden (y deberían) utilizar variables de entorno para almacenar los datos de conexión (dotenv) 118 | const client = new Client({ 119 | user: "", 120 | host: "", 121 | database: "", 122 | password: "", 123 | port: 5432, 124 | ssl: true 125 | }); 126 | 127 | client.connect(); // Nos conectamos a la base de datos 128 | ``` 129 | 130 | #### Consultas 131 | 132 | Una vez que se conectaron a la base de datos, podrán realizar consultas. Por ejemplo, para obtener todos los registros de una tabla: 133 | 134 | ```javascript 135 | const res = await client.query("SELECT * FROM "); 136 | console.log(res.rows); // Imprimimos los registros 137 | ``` 138 | 139 | Para insertar un registro en una tabla: 140 | 141 | ```javascript 142 | await client.query("INSERT INTO (columna1, columna2) VALUES ($1, $2)", [valor1, valor2]); 143 | ``` 144 | 145 | Notar que en el segundo argumento de `query` se pasan los valores que se quieren insertar en la tabla. Esto se escribe un poco distinto a los prepared statements de MySQL (utilizábamos el `?`). En este caso, se utilizan los `$1`, `$2`, etc. para referenciar a los valores que se quieren insertar, nos permite cambiar el orden de los valores sin tener que cambiar el orden de los `$`. 146 | -------------------------------------------------------------------------------- /public/assets/fontawesome/scss/_animated.scss: -------------------------------------------------------------------------------- 1 | // animating icons 2 | // -------------------------- 3 | 4 | .#{$fa-css-prefix}-beat { 5 | animation-name: #{$fa-css-prefix}-beat; 6 | animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0); 7 | animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); 8 | animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); 9 | animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); 10 | animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, ease-in-out); 11 | } 12 | 13 | .#{$fa-css-prefix}-bounce { 14 | animation-name: #{$fa-css-prefix}-bounce; 15 | animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0); 16 | animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); 17 | animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); 18 | animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); 19 | animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(0.280, 0.840, 0.420, 1)); 20 | } 21 | 22 | .#{$fa-css-prefix}-fade { 23 | animation-name: #{$fa-css-prefix}-fade; 24 | animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0); 25 | animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); 26 | animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); 27 | animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); 28 | animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(.4,0,.6,1)); 29 | } 30 | 31 | .#{$fa-css-prefix}-beat-fade { 32 | animation-name: #{$fa-css-prefix}-beat-fade; 33 | animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0); 34 | animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); 35 | animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); 36 | animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); 37 | animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(.4,0,.6,1)); 38 | } 39 | 40 | .#{$fa-css-prefix}-flip { 41 | animation-name: #{$fa-css-prefix}-flip; 42 | animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0); 43 | animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); 44 | animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); 45 | animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); 46 | animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, ease-in-out); 47 | } 48 | 49 | .#{$fa-css-prefix}-shake { 50 | animation-name: #{$fa-css-prefix}-shake; 51 | animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0); 52 | animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); 53 | animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); 54 | animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); 55 | animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, linear); 56 | } 57 | 58 | .#{$fa-css-prefix}-spin { 59 | animation-name: #{$fa-css-prefix}-spin; 60 | animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0); 61 | animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); 62 | animation-duration: var(--#{$fa-css-prefix}-animation-duration, 2s); 63 | animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); 64 | animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, linear); 65 | } 66 | 67 | .#{$fa-css-prefix}-spin-reverse { 68 | --#{$fa-css-prefix}-animation-direction: reverse; 69 | } 70 | 71 | .#{$fa-css-prefix}-pulse, 72 | .#{$fa-css-prefix}-spin-pulse { 73 | animation-name: #{$fa-css-prefix}-spin; 74 | animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); 75 | animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); 76 | animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); 77 | animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, steps(8)); 78 | } 79 | 80 | // if agent or operating system prefers reduced motion, disable animations 81 | // see: https://www.smashingmagazine.com/2020/09/design-reduced-motion-sensitivities/ 82 | // see: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion 83 | @media (prefers-reduced-motion: reduce) { 84 | .#{$fa-css-prefix}-beat, 85 | .#{$fa-css-prefix}-bounce, 86 | .#{$fa-css-prefix}-fade, 87 | .#{$fa-css-prefix}-beat-fade, 88 | .#{$fa-css-prefix}-flip, 89 | .#{$fa-css-prefix}-pulse, 90 | .#{$fa-css-prefix}-shake, 91 | .#{$fa-css-prefix}-spin, 92 | .#{$fa-css-prefix}-spin-pulse { 93 | animation-delay: -1ms; 94 | animation-duration: 1ms; 95 | animation-iteration-count: 1; 96 | transition-delay: 0s; 97 | transition-duration: 0s; 98 | } 99 | } 100 | 101 | @keyframes #{$fa-css-prefix}-beat { 102 | 0%, 90% { transform: scale(1); } 103 | 45% { transform: scale(var(--#{$fa-css-prefix}-beat-scale, 1.25)); } 104 | } 105 | 106 | @keyframes #{$fa-css-prefix}-bounce { 107 | 0% { transform: scale(1,1) translateY(0); } 108 | 10% { transform: scale(var(--#{$fa-css-prefix}-bounce-start-scale-x, 1.1),var(--#{$fa-css-prefix}-bounce-start-scale-y, 0.9)) translateY(0); } 109 | 30% { transform: scale(var(--#{$fa-css-prefix}-bounce-jump-scale-x, 0.9),var(--#{$fa-css-prefix}-bounce-jump-scale-y, 1.1)) translateY(var(--#{$fa-css-prefix}-bounce-height, -0.5em)); } 110 | 50% { transform: scale(var(--#{$fa-css-prefix}-bounce-land-scale-x, 1.05),var(--#{$fa-css-prefix}-bounce-land-scale-y, 0.95)) translateY(0); } 111 | 57% { transform: scale(1,1) translateY(var(--#{$fa-css-prefix}-bounce-rebound, -0.125em)); } 112 | 64% { transform: scale(1,1) translateY(0); } 113 | 100% { transform: scale(1,1) translateY(0); } 114 | } 115 | 116 | @keyframes #{$fa-css-prefix}-fade { 117 | 50% { opacity: var(--#{$fa-css-prefix}-fade-opacity, 0.4); } 118 | } 119 | 120 | @keyframes #{$fa-css-prefix}-beat-fade { 121 | 0%, 100% { 122 | opacity: var(--#{$fa-css-prefix}-beat-fade-opacity, 0.4); 123 | transform: scale(1); 124 | } 125 | 50% { 126 | opacity: 1; 127 | transform: scale(var(--#{$fa-css-prefix}-beat-fade-scale, 1.125)); 128 | } 129 | } 130 | 131 | @keyframes #{$fa-css-prefix}-flip { 132 | 50% { 133 | transform: rotate3d(var(--#{$fa-css-prefix}-flip-x, 0), var(--#{$fa-css-prefix}-flip-y, 1), var(--#{$fa-css-prefix}-flip-z, 0), var(--#{$fa-css-prefix}-flip-angle, -180deg)); 134 | } 135 | } 136 | 137 | @keyframes #{$fa-css-prefix}-shake { 138 | 0% { transform: rotate(-15deg); } 139 | 4% { transform: rotate(15deg); } 140 | 8%, 24% { transform: rotate(-18deg); } 141 | 12%, 28% { transform: rotate(18deg); } 142 | 16% { transform: rotate(-22deg); } 143 | 20% { transform: rotate(22deg); } 144 | 32% { transform: rotate(-12deg); } 145 | 36% { transform: rotate(12deg); } 146 | 40%, 100% { transform: rotate(0deg); } 147 | } 148 | 149 | @keyframes #{$fa-css-prefix}-spin { 150 | 0% { transform: rotate(0deg); } 151 | 100% { transform: rotate(360deg); } 152 | } 153 | 154 | -------------------------------------------------------------------------------- /cheatsheets/cloudinary-multer.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Subir Imagenes (Multer & Cloudinary) 3 | image: "/assets/images/cloudinary.svg" 4 | --- 5 | 6 | ## Índice 7 | 8 | - [Índice](#índice) 9 | - [Introducción](#introducción) 10 | - [Crear Cuenta (`Cloudinary`)](#crear-cuenta-cloudinary) 11 | - [Instalación](#instalación) 12 | - [Configuracion (Multer)](#configuracion-multer) 13 | - [Conexion (Cloudinary)](#conexion-cloudinary) 14 | - [Almacenamiento de Fotos (Local)](#almacenamiento-de-fotos-local) 15 | - [Subida a la nube con Cloudinary](#subida-a-la-nube-con-cloudinary) 16 | - [Archivos temporales con Multer (para subir a la nube)](#archivos-temporales-con-multer-para-subir-a-la-nube) 17 | 18 |
19 | 20 | ## Introducción 21 | 22 | Con estas dos herramientas, podremos subir imágenes a Cloudinary con el objetivo de obtener el enlace público para acceder a ellas desde cualquier lugar. Utilizaremos `Multer` para recibir los archivos de fotos y, con la conexión a `Cloudinary`, podremos subirlas. 23 | 24 | ## Crear Cuenta (`Cloudinary`) 25 | 26 | Primero, debemos crear una cuenta en `Cloudinary` para poder subir nuestras fotos. Es recomendable hacerlo utilizando GitHub. 27 | 28 | URL: https://console.cloudinary.com/users/register_free 29 | 30 | ## Instalación 31 | 32 | Para instalar `Multer` y `Cloudinary`, deberemos ejecutar los siguientes comandos en la consola (dentro de un proyecto de Node.js): 33 | 34 | ```bash 35 | npm i cloudinary 36 | npm i multer 37 | ``` 38 | 39 | ## Configuracion (Multer) 40 | 41 | Para configurar `Multer`, primero debemos crear una carpeta donde se almacenarán las imágenes localmente (ya sea de forma temporal o permanente). Esta carpeta debe estar ubicada dentro del proyecto, ya sea dentro de la carpeta src o en una carpeta separada. 42 | 43 | La configuración de `Multer` debe estar en el router de la ruta donde deseamos trabajar. Por ejemplo, si queremos gestionar la subida de fotos de perfil, configuraremos `Multer` en el router de usuarios. 44 | 45 | Primero, debemos importar el módulo de `Multer`: 46 | 47 | ```js 48 | import multer from 'multer'; 49 | ``` 50 | 51 | A continuación, debemos realizar la configuración de `Multer`. El código debería verse así: 52 | 53 | ```js 54 | const __filename = fileURLToPath(import.meta.url); 55 | const __dirname = dirname(__filename); 56 | 57 | // Poner la ubicacion de la carpeta de Uploads correspondiente, en este caso se ubica dentro del SRC 58 | const uploadDir = join(__dirname, "../uploads"); 59 | 60 | // Se define donde se va a ubicar el archivo que vamos a subir y el nombre, este se puede modificar, en este caso el nombre que se le va a asignar es la fecha de subida sumado del nombre del archivo original 61 | const storage = multer.diskStorage({ 62 | destination: function (req, file, cb) { 63 | cb(null, uploadDir); 64 | }, 65 | filename: function (req, file, cb) { 66 | cb(null, `${Date.now()}-${file.originalname}`) 67 | } 68 | }); 69 | 70 | // El siguiente filtro es para que se suban unicamente archivos con extensiones especificas. En este caso serian JPEG, PNG y JPG 71 | const fileFilter = (req, file, cb) => { 72 | const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg']; 73 | if (allowedTypes.includes(file.mimetype)) { 74 | cb(null, true); 75 | } else { 76 | cb(new Error('Invalid file type. Only PDF, PNG, JPEG, and JPG files are allowed.'), false); 77 | } 78 | }; 79 | 80 | const upload = multer({ 81 | storage: storage, 82 | fileFilter: fileFilter 83 | }); 84 | ``` 85 | 86 | ## Conexion (Cloudinary) 87 | 88 | Para realizar la conexión con `Cloudinary`, debemos acceder a nuestro dashboard personal de `Cloudinary` en el siguiente enlace para obtener nuestras credenciales. 89 | 90 | Desde ahí, podemos acceder a nuestro Cloudname, que se encuentra en el centro de la página, y debemos copiarlo. A continuación, hacemos clic en el botón 'Go to API Keys', ubicado a la derecha del Cloudname, donde encontraremos la API Key y el API Secret, los cuales también debemos copiar. 91 | 92 | En nuestro proyecto, debemos crear un archivo llamado `upload.js`, donde configuraremos toda la conexión. 93 | 94 | ```js 95 | import { v2 as cloudinary } from 'cloudinary'; 96 | 97 | cloudinary.config({ 98 | cloud_name: "", 99 | api_key: "", 100 | api_secret: "" 101 | }); 102 | 103 | export default cloudinary; 104 | ``` 105 | 106 | ## Almacenamiento de Fotos (Local) 107 | 108 | Para que los archivos se suban localmente, primero debemos definir desde qué ruta lo haremos, configurándolo en el router correspondiente. El siguiente ejemplo es para la creación de usuarios, pero puede modificarse para otras rutas según sea necesario. 109 | 110 | ```js 111 | // Crear Usuarios 112 | router.post("/registerUsers", upload.single('file'), registerController.registerUsers); 113 | ``` 114 | 115 | De esta manera, cuando se suban las imágenes, se guardarán en la carpeta `uploads` que hemos creado. 116 | 117 | Además, desde el controlador también podemos controlar las extensiones de los archivos subidos utilizando el siguiente código: 118 | 119 | ```js 120 | // Crear Usuarios 121 | const imageFile = req.file.path; 122 | 123 | const extension = imageFile.split('.').pop(); 124 | const extensionesPermitidas = ['pdf', 'png', 'jpeg', 'jpg']; 125 | 126 | if (!extensionesPermitidas.includes(extension)) { 127 | console.error('Extensión de archivo no permitida'); 128 | return res.status(400).send('Error: Extensión de archivo no permitida. Extensiones admitidas: PDF, PNG, JPEG, y JPG'); 129 | } 130 | ``` 131 | 132 | ## Subida a la nube con Cloudinary 133 | 134 | Para subir las imágenes a `Cloudinary`, debemos importar la configuración desde el archivo que habíamos creado (`upload.js`) utilizando el siguiente código en el controlador donde vamos a trabajar: 135 | 136 | ```js 137 | import cloudinary from '../upload.js'; 138 | ``` 139 | 140 | Para subir las imágenes a `Cloudinary`, dentro del controlador donde estemos trabajando, debemos usar el `imageFile` que estábamos gestionando con `Multer`. Luego, debemos ejecutar la siguiente función para subir la imagen, la cual nos devolverá un JSON con la información. Con esta información, podemos almacenar la URL de donde se ubicará la imagen: 141 | 142 | ```js 143 | const imageFile = req.file.path; 144 | 145 | const result = await cloudinary.uploader.upload(imageFile, { 146 | folder: 'analisis', 147 | }); 148 | 149 | const imageUrl = result.secure_url; 150 | ``` 151 | 152 | A continuación, podemos guardar el enlace en la base de datos de la siguiente forma: 153 | 154 | ```js 155 | const query = 'INSERT INTO public.users (imagen) VALUES ($1)'; 156 | 157 | await client.query(query, [imageUrl]); 158 | ``` 159 | 160 | ## Archivos temporales con Multer (para subir a la nube) 161 | 162 | Para que los archivos sean temporales, simplemente importa el módulo `fs` en el controlador y ejecuta la función `unlinkSync` para eliminar los archivos después de que ya hayan sido procesados. Aquí tienes un ejemplo de cómo hacerlo: 163 | 164 | ```js 165 | import fs from "fs"; 166 | 167 | const imageFile = req.file.path; 168 | 169 | const result = await cloudinary.uploader.upload(imageFile, { 170 | folder: 'analisis', 171 | }); 172 | 173 | const imageUrl = result.secure_url; 174 | 175 | const query = 'INSERT INTO public.users (imagen) VALUES ($1)'; 176 | 177 | await client.query(query, [imageUrl]); 178 | 179 | fs.unlinkSync(imageFile); // Eliminar el archivo local 180 | ``` -------------------------------------------------------------------------------- /pages/index.tsx: -------------------------------------------------------------------------------- 1 | import type { NextPage } from "next"; 2 | import Head from "next/head"; 3 | import fs from "fs"; 4 | import path from "path"; 5 | import matter from "gray-matter"; 6 | import GitHubButton from "react-github-btn"; 7 | import Background from "../components/layout/Background"; 8 | import Terminal from "../components/utils/Terminal/Terminal"; 9 | import Glassbox from "../components/utils/Glassbox"; 10 | import Section from "../components/layout/Section"; 11 | import { useWindowSize } from "usehooks-ts"; 12 | import Image from "next/image"; 13 | import { useMemo } from "react"; 14 | 15 | export type CheatsheetType = { 16 | slug: string; 17 | frontmatter: { 18 | title: string; 19 | image: string; 20 | }; 21 | }; 22 | 23 | const Home: NextPage = ({ cheatsheets }: { cheatsheets: CheatsheetType[] }) => { 24 | const windowSize = useWindowSize(); 25 | 26 | const ratio = 20 / 17; 27 | 28 | const imageWidth = useMemo(() => { 29 | if (windowSize.width >= 1900) return 800; 30 | if (windowSize.width >= 1450) return 600; 31 | if (windowSize.width >= 1250) return 400; 32 | if (windowSize.width >= 1000) return 380; 33 | if (windowSize.width >= 800) return 320; 34 | if (windowSize.width >= 500) return 250; 35 | return 200; 36 | }, [windowSize]); 37 | 38 | const imageHeight = useMemo(() => imageWidth / ratio, [imageWidth]); 39 | 40 | return ( 41 | <> 42 |
43 | 44 | {"<TIC_Cheatsheets/>"} 45 | 46 |
47 | 69 |
70 |
71 | 75 |
76 |

77 | {"¿Qué es ?"} 78 |

79 | 86 |
87 |

88 | En esta web se encuentra una colección de 89 | "hojas de trucos" a las que se puede 90 | recurrir a la hora de programar en cualquiera de los 91 | lenguajes disponibles. La idea es que ésta sea de 92 | realización colectiva, es decir, que todos (tanto 93 | profesores como alumnos) puedan hacer su aporte a 94 | los cheatsheets, con el objetivo de aumentar la 95 | calidad y la cantidad de la información. Para 96 | aportar algún cambio o un cheatsheet nuevo, es muy 97 | importante que leas el README del repositorio. 98 |

99 |
100 |

101 | Si este repositorio te sirve podés darle una 102 | estrellita{" "} 103 |

104 | 111 | Star 112 | 113 |
114 |
115 |
116 |
117 | 118 |
119 |
120 | 121 | ); 122 | }; 123 | 124 | export const getStaticProps = async () => { 125 | const files = fs.readdirSync(path.join("cheatsheets")); 126 | 127 | const cheatsheets = files.map((file) => { 128 | const slug = file.replace(".md", ""); 129 | 130 | const markdownWithMeta = fs.readFileSync( 131 | path.join("cheatsheets", file), 132 | "utf8" 133 | ); 134 | 135 | const { data: frontmatter } = matter(markdownWithMeta); 136 | 137 | return { 138 | slug, 139 | frontmatter, 140 | } as CheatsheetType; 141 | }); 142 | 143 | return { 144 | props: { 145 | cheatsheets, 146 | }, 147 | }; 148 | }; 149 | 150 | export default Home; 151 | -------------------------------------------------------------------------------- /components/utils/Terminal/Command.tsx: -------------------------------------------------------------------------------- 1 | import { useRouter } from "next/router"; 2 | import { useContext, useEffect, useRef, useState } from "react"; 3 | import { TerminalContext } from "../../../contexts/TerminalContext"; 4 | import { CheatsheetType } from "../../../pages"; 5 | import { AutocompleteCommand, AutocompleteFile } from "./Autocomplete"; 6 | import useCommandList from "./useCommandList"; 7 | 8 | const Command = ({ 9 | index, 10 | initialCommand, 11 | active, 12 | cheatsheets, 13 | }: { 14 | initialCommand: string; 15 | index: number; 16 | active: boolean; 17 | cheatsheets: CheatsheetType[]; 18 | }) => { 19 | const [copyCommand, setCopyCommand] = useState(index); 20 | const [text, setText] = useState(initialCommand); 21 | const [currentCommand, setCurrentCommand] = useState(""); 22 | const router = useRouter(); 23 | 24 | const { 25 | triggerError, 26 | errorMsg, 27 | addCommand, 28 | changeCommand, 29 | repeatCommand, 30 | clearCommands, 31 | } = useContext(TerminalContext); 32 | 33 | const commandList = useCommandList(cheatsheets, router, clearCommands); 34 | const containerRef = useRef(null); 35 | 36 | const addResponse = (content: string) => { 37 | const response = document.createElement("div"); 38 | response.className = "text-[#9FEA18] font-normal"; 39 | response.innerHTML = content; 40 | containerRef.current?.appendChild(response); 41 | }; 42 | 43 | const suggestCommand = (input: HTMLInputElement) => { 44 | const command = Object.keys(commandList).find((c) => 45 | c.startsWith(input.value) 46 | ); 47 | 48 | if (command) { 49 | input.value = command; 50 | changeCommand(command, index); 51 | setCurrentCommand(command); 52 | } else { 53 | setText(""); 54 | } 55 | }; 56 | 57 | const suggestFile = (input: HTMLInputElement) => { 58 | const [commandName, ...args] = input.value.split(" "); 59 | const command = commandList[commandName]; 60 | 61 | if (!command) { 62 | triggerError(); 63 | return; 64 | } 65 | 66 | if (command.type !== "action" && command.type !== "voidAction") { 67 | triggerError(); 68 | return; 69 | } 70 | 71 | const file = cheatsheets.find((f) => 72 | (f.slug + ".md").startsWith(args[0]) 73 | )?.slug as string; 74 | 75 | if (file) { 76 | input.value = `${commandName} ${file}.md`; 77 | changeCommand(`${commandName} ${file}.md`, index); 78 | setCurrentCommand(`${commandName} ${file}.md`); 79 | } else { 80 | triggerError(); 81 | } 82 | }; 83 | 84 | const parseCommand = (inputValue: string) => { 85 | const [commandName, ...args] = inputValue.split(" "); 86 | const command = commandList[commandName]; 87 | 88 | if (!command) { 89 | triggerError(); 90 | return; 91 | } 92 | 93 | if (command.args && command.args.length !== args.length) { 94 | triggerError(); 95 | return; 96 | } 97 | 98 | if (!command.args && args.length > 0) { 99 | triggerError(); 100 | return; 101 | } 102 | 103 | if (command.type === "voidAction") { 104 | let result: boolean; 105 | if (command.usesFileNames) { 106 | result = command.action( 107 | ...args, 108 | cheatsheets.map((c) => c.slug + ".md") 109 | ) as boolean; 110 | } else { 111 | result = command.action(...args) as boolean; 112 | } 113 | if (result) { 114 | if (!command.preventAdd) addCommand(); 115 | } else { 116 | triggerError(); 117 | } 118 | } else if (command.type === "action") { 119 | addResponse(command.action(...args) as string); 120 | addCommand(); 121 | } else if (command.type === "text") { 122 | addResponse(command.content); 123 | addCommand(); 124 | } 125 | }; 126 | 127 | const handleKeyDown = (e: React.KeyboardEvent) => { 128 | const input = e.target as HTMLInputElement; 129 | 130 | if (e.key === "Tab") { 131 | e.preventDefault(); 132 | if (input.value.split(" ").length > 1) { 133 | suggestFile(input); 134 | } else { 135 | suggestCommand(input); 136 | } 137 | } else if (e.code == "Space") { 138 | const command = Object.keys(commandList).find((c) => 139 | c.startsWith(input.value.split(" ")[0]) 140 | ); 141 | 142 | if (command) { 143 | changeCommand(command, index); 144 | setCurrentCommand(command); 145 | } 146 | } else if (e.key === "Enter") { 147 | e.preventDefault(); 148 | parseCommand(input.value); 149 | } else if (e.key === "ArrowUp") { 150 | e.preventDefault(); 151 | if (copyCommand === 0) return; 152 | input.value = repeatCommand(copyCommand - 1); 153 | setCopyCommand(copyCommand - 1); 154 | } else if (e.key === "ArrowDown") { 155 | e.preventDefault(); 156 | if (copyCommand === index) return; 157 | input.value = repeatCommand(copyCommand + 1); 158 | setCopyCommand(copyCommand + 1); 159 | } else if (e.key === "l" && e.ctrlKey) { 160 | e.preventDefault(); 161 | clearCommands(); 162 | } 163 | }; 164 | 165 | const handleChange = (e: React.ChangeEvent) => { 166 | const input = e.target as HTMLInputElement; 167 | setText(input.value); 168 | }; 169 | 170 | return ( 171 |
175 |
176 | ~/ORT/TIC/cheatsheets{" "} 177 | git: 178 | (main) 179 |
180 |
181 | {">"} 182 | 196 | 200 | {currentCommand === "open" || 201 | currentCommand === "export" || 202 | currentCommand === "cat" ? ( 203 | c.slug + ".md")]} 206 | /> 207 | ) : null} 208 |
209 | {errorMsg && active && ( 210 |
{errorMsg}
211 | )} 212 |
213 | ); 214 | }; 215 | 216 | export default Command; 217 | -------------------------------------------------------------------------------- /components/utils/Terminal/useCommandList.tsx: -------------------------------------------------------------------------------- 1 | type CommandItem = { 2 | type: "text" | "voidAction" | "action"; 3 | content?: string; 4 | action?: 5 | | ((...args: any[]) => void) 6 | | ((...args: any[]) => string) 7 | | ((...args: any[]) => boolean); 8 | args?: string[]; 9 | preventAdd?: boolean; 10 | man: string; 11 | usesFileNames?: boolean; 12 | }; 13 | 14 | type CommandList = { 15 | [key: string]: CommandItem; 16 | }; 17 | 18 | type CommandListFunction = ( 19 | cheatsheets: any[], 20 | router: any, 21 | clearCommands: () => void 22 | ) => CommandList; 23 | 24 | const useCommandList: CommandListFunction = ( 25 | cheatsheets, 26 | router, 27 | clearCommands 28 | ) => { 29 | const openFile = (slug: string, validFileNames: string[]) => { 30 | if (!validFileNames.includes(slug)) return false; 31 | 32 | const link = slug.split(".")[0]; 33 | router.push(`/cheatsheet/${link}`); 34 | return true; 35 | } 36 | 37 | const commands: CommandList = { 38 | help: { 39 | type: "text", 40 | content: ` 41 | Comandos disponibles: 42 |
43 |
    44 |
  • - about
  • 45 |
  • - cls
  • 46 |
  • - code
  • 47 |
  • - export
  • 48 |
  • - github
  • 49 |
  • - help
  • 50 |
  • - ls
  • 51 |
  • - open
  • 52 |
  • - pwd
  • 53 |
  • - man
  • 54 |
  • - shutdown
  • 55 |
56 |
57 | Para saber cómo usarlos y qué hacen, podes utilizar 'man [comando]' 58 | `, 59 | man: ` 60 | help 61 |
62 | 63 | Muestra una lista de los comandos disponibles. 64 | 65 | `, 66 | }, 67 | about: { 68 | type: "text", 69 | content: ` 70 | ¿Qué es <TIC Cheatsheets/>? 71 |
72 | 73 | En esta web se encuentra una colección de 74 | "hojas de trucos" a las que se puede 75 | recurrir a la hora de programar en cualquiera de los 76 | lenguajes disponibles. La idea es que ésta sea de 77 | realización colectiva, es decir, que todos (tanto 78 | profesores como alumnos) puedan hacer su aporte a 79 | los cheatsheets, con el objetivo de aumentar la 80 | calidad y la cantidad de la información. Para 81 | aportar algún cambio o un cheatsheet nuevo, es muy 82 | importante que leas el README del repositorio. 83 | `, 84 | man: ` 85 | about 86 |
87 | 88 | Muestra información sobre la web. 89 | 90 | `, 91 | }, 92 | github: { 93 | type: "voidAction", 94 | action: () => 95 | window.open( 96 | "https://github.com/nachovigilante/cheatsheets", 97 | "_blank" 98 | ), 99 | man: ` 100 | github 101 |
102 | 103 | Abre el repositorio de la web en una nueva pestaña. 104 | 105 | `, 106 | }, 107 | open: { 108 | type: "voidAction", 109 | action: openFile, 110 | args: ["slug"], 111 | man: ` 112 | open [slug].md 113 |
114 | 115 | Abre el cheatsheet con el slug indicado (igual que cat). 116 | 117 | `, 118 | usesFileNames: true, 119 | }, 120 | cat: { 121 | type: "voidAction", 122 | action: openFile, 123 | args: ["slug"], 124 | man: ` 125 | cat [slug].md 126 |
127 | 128 | Abre el cheatsheet con el slug indicado (igual que open). 129 | 130 | `, 131 | usesFileNames: true, 132 | }, 133 | export: { 134 | type: "voidAction", 135 | action: (slug: string, validFileNames: string[]) => { 136 | if (!validFileNames.includes(slug)) return false; 137 | 138 | const link = slug.split(".")[0]; 139 | const anchor = document.createElement("a"); 140 | anchor.href = `/download/${link}.pdf`; 141 | anchor.download = `${link}.pdf`; 142 | document.body.appendChild(anchor); 143 | anchor.click(); 144 | return true; 145 | }, 146 | args: ["slug"], 147 | man: ` 148 | export [slug] 149 |
150 | 151 | Descarga el cheatsheet con el slug indicado. 152 | 153 | `, 154 | usesFileNames: true, 155 | }, 156 | cls: { 157 | type: "voidAction", 158 | action: () => { 159 | clearCommands(); 160 | return true; 161 | }, 162 | preventAdd: true, 163 | man: ` 164 | cls 165 |
166 | 167 | Limpia la terminal. 168 | 169 | `, 170 | }, 171 | ls: { 172 | type: "text", 173 | content: ` 174 |
    175 | ${ 176 | cheatsheets 177 | .map((c) => `
  • ${c.slug}.md
  • `) 178 | .join("") || "No hay cheatsheets disponibles" 179 | } 180 |
181 | `, 182 | man: ` 183 | ls 184 |
185 | 186 | Muestra una lista de los cheatsheets disponibles. 187 | 188 | `, 189 | }, 190 | pwd: { 191 | type: "text", 192 | content: "tic://cheatsheets/", 193 | man: ` 194 | pwd 195 |
196 | 197 | Muestra la ruta actual. 198 | 199 | `, 200 | }, 201 | code: { 202 | type: "voidAction", 203 | action: () => { 204 | window.open( 205 | "https://vscode.dev/github/nachovigilante/cheatsheets", 206 | "_blank" 207 | ); 208 | }, 209 | man: ` 210 | code 211 |
212 | 213 | Abre el repositorio de la web en VS Code. 214 | 215 | `, 216 | }, 217 | shutdown: { 218 | type: "voidAction", 219 | action: () => { 220 | window.open( 221 | "https://www.youtube.com/watch?v=dQw4w9WgXcQ", 222 | "_blank" 223 | ); 224 | }, 225 | man: ` 226 | shutdown 227 |
228 | 229 | Apaga la terminal. 230 | 231 | `, 232 | }, 233 | man: { 234 | type: "action", 235 | action: (command: string) => { 236 | console.log("AAAAAAAA"); 237 | const commandItemKey = Object.keys(commands).find( 238 | (c) => c === command 239 | ); 240 | const commandItem = commands[commandItemKey]; 241 | if (commandItem) { 242 | return commandItem.man; 243 | } 244 | return "No se ha encontrado el comando indicado."; 245 | }, 246 | args: ["command"], 247 | man: ` 248 | man [command] 249 |
250 | 251 | Muestra la documentación del comando indicado. 252 | 253 | `, 254 | }, 255 | }; 256 | 257 | return commands; 258 | }; 259 | 260 | export default useCommandList; 261 | -------------------------------------------------------------------------------- /cheatsheets/react.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: React 3 | image: "/assets/images/react.svg" 4 | --- 5 | 6 | ## Índice 7 | 8 | - [Índice](#índice) 9 | - [Elementos](#elementos) 10 | - [Sintaxis de un elemento](#sintaxis-de-un-elemento) 11 | - [Atributos de un elemento](#atributos-de-un-elemento) 12 | - [Componentes](#componentes) 13 | - [Fragmentos de React](#fragmentos-de-react) 14 | - [Propiedades](#propiedades) 15 | - [Condicionales](#condicionales) 16 | - [Operadores ternarios](#operadores-ternarios) 17 | - [If](#if) 18 | - [Listas](#listas) 19 | - [Hooks](#hooks) 20 | - [useState](#usestate) 21 | - [useEffect](#useeffect) 22 | - [useContext](#usecontext) 23 | - [useRef](#useref) 24 | - [useMemo](#usememo) 25 | - [useCallback](#usecallback) 26 | 27 |
28 | 29 | ## Elementos 30 | 31 | ### Sintaxis de un elemento 32 | 33 | Los elementos de React se escriben igual que los de HTML, hay algunas diferencias pero dentro de React se puede escribir HTML puro. 34 |

35 | Una diferencia con HTML, por ejemplo, es que no se pueden escribir elementos que no se cierran, como imagenes o saltos de línea. 36 | 37 | Por ejemplo, en HTML una imagen podría usarse así 38 | 39 | ```html 40 | imagen 41 | ``` 42 | 43 | Pero en React debe cerrarse ese tag, de esta manera 44 | 45 | ```html 46 | imagen 47 | ``` 48 | 49 | ### Atributos de un elemento 50 | 51 | Los atributos en React no pueden contener caracteres especiales, por lo que algunos serán diferentes a los de HTML. 52 | 53 | Un cambio importante en React es el del atributo clase, el cual cambia de `class` a `className`. 54 | 55 | HTML: 56 | 57 | ```html 58 |
59 | ``` 60 | 61 | React: 62 | 63 | ```html 64 |
65 | ``` 66 | 67 | ## Componentes 68 | 69 | Los componentes de React pueden ser escritos como clases o como funciones. 70 | 71 | Un componente funcional de React se expresa de la siguiente manera 72 | 73 | ```javascript 74 | function App() { 75 | return
Hello world!
; 76 | } 77 | ``` 78 | 79 | Los componentes de React deben empezar con una letra mayúscula obligatoriamente y devolver código JSX (HTML de React) 80 | 81 | ### Fragmentos de React 82 | 83 | Los componentes de React deben devolver un único elemento. Por ejemplo, esto no es válido. 84 | 85 | ```javascript 86 | function App() { 87 | return ( 88 |
Hello world!
89 |
Hello world!
90 | ); 91 | } 92 | ``` 93 | 94 | Si se quiere devolver más de un elemento sin encerrarlo en otro, como un div, se debe usar un Fragmento, el cual puede expresarse de 2 maneras. 95 | 96 | ```javascript 97 | function App() { 98 | return ( 99 | <> 100 |
Hello world!
101 |
Hello world!
102 | 103 | ); 104 | } 105 | ``` 106 | 107 | ```javascript 108 | function App() { 109 | return ( 110 | 111 |
Hello world!
112 |
Hello world!
113 |
114 | ); 115 | } 116 | ``` 117 | 118 | ## Propiedades 119 | 120 | Al crear componentes en React, podemos añadirle propiedades con el parametro props, el cual recibe todos los atributos que se le pasan al crearlo en otro componente. 121 | 122 | Por ejemplo, en el componente App puedo crear el componente User y pasar la propiedad nombre para que se muestre ese texto en el otro componente. 123 | 124 | ```javascript 125 | function App() { 126 | return ; 127 | } 128 | 129 | function User(props) { 130 | return

Hola, {props.nombre}

; 131 | } 132 | ``` 133 | 134 | Esto resultará en 135 | 136 | ```html 137 |

Hola, John Doe

138 | ``` 139 | 140 | Hay algunas propiedades que se pasan por defecto, como la propiedad children, la cual contiene los elementos que pusiste adentro del componente. 141 | 142 | Por ejemplo 143 | 144 | ```javascript 145 | function App() { 146 | return ( 147 | 148 |

Hola, John Doe

149 |
150 | ); 151 | } 152 | 153 | function User(props) { 154 | return
{props.children}
; 155 | } 156 | ``` 157 | 158 | Esto resultará en 159 | 160 | ```html 161 |
162 |

Hola, John Doe

163 |
164 | ``` 165 | 166 | ## Condicionales 167 | 168 | Los condicionales en React se pueden hacer de 2 maneras, con operadores ternarios o con if. 169 | 170 | ### Operadores ternarios 171 | 172 | Los operadores ternarios se pueden usar para mostrar un componente si se cumple una condición. 173 | 174 | Por ejemplo, si quiero mostrar un componente si el usuario está logueado, puedo usar un operador ternario de la siguiente manera 175 | 176 | ```javascript 177 | function App() { 178 | const isLogged = true; 179 | return
{isLogged ? : }
; 180 | } 181 | ``` 182 | 183 | ### If 184 | 185 | Los condicionales if se pueden usar para mostrar un componente si se cumple una condición. 186 | 187 | Por ejemplo, si quiero mostrar un componente si el usuario está logueado, puedo usar un if de la siguiente manera 188 | 189 | ```javascript 190 | function App() { 191 | const isLogged = true; 192 | 193 | if (isLogged) { 194 | return ; 195 | } else { 196 | return ; 197 | } 198 | } 199 | ``` 200 | 201 | ## Listas 202 | 203 | Para crear una lista con un array, se debe usar el método map, el cual recibe una función que se ejecutará por cada elemento del array. 204 | 205 | Por ejemplo, si quiero mostrar una lista de usuarios, puedo usar un array de la siguiente manera 206 | 207 | ```javascript 208 | function App() { 209 | const users = ["John Doe", "Jane Doe", "Jack Doe"]; 210 | 211 | return ( 212 |
213 | {users.map((user) => ( 214 | 215 | ))} 216 |
217 | ); 218 | } 219 | ``` 220 | 221 | ## Hooks 222 | 223 | Los hooks son funciones que nos permiten usar características de React sin tener que crear un componente. Acá hay una lista de los hooks mas conocidos. 224 | 225 | ### useState 226 | 227 | El hook useState nos permite crear variables de estado, las cuales se pueden modificar y React se encargará de actualizar el componente. 228 | 229 | Por ejemplo, si quiero crear una variable de estado que se llame count y que empiece en 0, puedo usar el hook useState de la siguiente manera 230 | 231 | ```javascript 232 | function App() { 233 | const [count, setCount] = useState(0); 234 | return ( 235 |
236 |

Count: {count}

237 | 238 |
239 | ); 240 | } 241 | ``` 242 | 243 | ### useEffect 244 | 245 | El hook useEffect nos permite ejecutar código cuando se renderiza el componente o cuando se actualiza una variable de estado. 246 | 247 | Por ejemplo, si quiero ejecutar un código cuando se renderiza el componente, puedo usar el hook useEffect de la siguiente manera 248 | 249 | ```javascript 250 | function App() { 251 | useEffect(() => { 252 | console.log("Se renderizó el componente"); 253 | }, []); 254 | return ( 255 |
256 |

Count: {count}

257 | 258 |
259 | ); 260 | } 261 | ``` 262 | 263 | Si quiero ejecutar un código cuando se actualiza una variable de estado, puedo usar el hook useEffect de la siguiente manera 264 | 265 | ```javascript 266 | function App() { 267 | const [count, setCount] = useState(0); 268 | useEffect(() => { 269 | console.log("Se actualizó la variable count"); 270 | }, [count]); 271 | return ( 272 |
273 |

Count: {count}

274 | 275 |
276 | ); 277 | } 278 | ``` 279 | 280 | ### useContext 281 | 282 | El hook useContext nos permite usar el contexto de un componente padre en un componente hijo. 283 | 284 | Por ejemplo, si quiero usar el contexto de un componente padre en un componente hijo, puedo usar el hook useContext de la siguiente manera 285 | 286 | ```javascript 287 | const UserContext = React.createContext(); 288 | 289 | function App() { 290 | return ( 291 | 292 | 293 | 294 | ); 295 | } 296 | 297 | function User() { 298 | const user = useContext(UserContext); 299 | return

Hola, {user}

; 300 | } 301 | ``` 302 | 303 | ### useRef 304 | 305 | El hook useRef nos permite crear una referencia a un elemento del DOM. 306 | 307 | Por ejemplo, si quiero crear una referencia a un elemento del DOM, puedo usar el hook useRef de la siguiente manera 308 | 309 | ```javascript 310 | function App() { 311 | const inputRef = useRef(null); 312 | return ( 313 |
314 | 315 | 316 |
317 | ); 318 | } 319 | ``` 320 | 321 | ### useMemo 322 | 323 | El hook useMemo nos permite crear una variable que se actualiza solo cuando una variable de estado cambia. 324 | 325 | Por ejemplo, si quiero crear una variable que se actualiza solo cuando el contador cambia, puedo usar el hook useMemo de la siguiente manera 326 | 327 | ```javascript 328 | function App() { 329 | const [count, setCount] = useState(0); 330 | const countDoble = useMemo(() => { 331 | return count * 2; 332 | }, [count]); 333 | return ( 334 |
335 |

Count: {count}

336 |

Count doble: {countDoble}

337 | 338 |
339 | ); 340 | } 341 | ``` 342 | 343 | ### useCallback 344 | 345 | El hook useCallback nos permite crear una función que se actualiza solo cuando una variable de estado cambia. 346 | 347 | Por ejemplo, si quiero crear una función que se actualiza solo cuando el contador cambia, puedo usar el hook useCallback de la siguiente manera 348 | 349 | ```javascript 350 | function App() { 351 | const [count, setCount] = useState(0); 352 | const incrementar = useCallback(() => { 353 | setCount(count + 1); 354 | }, [count]); 355 | return ( 356 |
357 |

Count: {count}

358 | 359 |
360 | ); 361 | } 362 | ``` 363 | -------------------------------------------------------------------------------- /cheatsheets/express.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: express.js 3 | image: "/assets/images/express.svg" 4 | --- 5 | 6 | ## Índice 7 | 8 | - [Índice](#índice) 9 | - [Instalación](#instalación) 10 | - [Creación de un servidor](#creación-de-un-servidor) 11 | - [Rutas](#rutas) 12 | - [Requests](#requests) 13 | - [Métodos HTTP](#métodos-http) 14 | - [GET](#get) 15 | - [Query parameters](#query-parameters) 16 | - [Path parameters](#path-parameters) 17 | - [POST](#post) 18 | - [Body parameters](#body-parameters) 19 | - [PUT](#put) 20 | - [DELETE](#delete) 21 | - [Responses](#responses) 22 | - [Métodos de respuesta](#métodos-de-respuesta) 23 | 24 |
25 | 26 | ## Instalación 27 | 28 | Para instalar Express deberemos ejecutar el siguiente comando (dentro de un proyecto de Node ya inicializado): 29 | 30 | ```bash 31 | npm install express 32 | ``` 33 | 34 | ## Creación de un servidor 35 | 36 | Para crear un servidor con Express deberemos crear un archivo `index.js` y añadir el siguiente código (recordar que para utilizar `import` deben tener la configuración `"type": "module"` en el archivo `package.json`): 37 | 38 | ```js 39 | // Importamos la librería express 40 | import express from "express"; 41 | // Creamos el servidor de Express con la configuración estándar básica 42 | const app = express(); 43 | 44 | // Asociamos la ruta "/" a la función pasada como segundo parámetro 45 | app.get("/", (req, res) => { 46 | // Esto envía el texto "Hello World!" como respuesta a la HTTP request 47 | res.send("Hello World!"); 48 | }); 49 | 50 | // Iniciamos el servidor en el puerto 3000 51 | app.listen(3000, () => { 52 | console.log("Example app listening on port 3000!"); 53 | }); 54 | ``` 55 | 56 | Luego se puede ejecutar con el siguiente comando: 57 | 58 | ```bash 59 | node index.js 60 | ``` 61 | 62 | ## Rutas 63 | 64 | Las rutas son lo que permiten al cliente utilizar diferentes URLs para hacer uso de diferentes funcionalidades de nuestro programa. Para añadir una ruta a nuestro servidor, simplemente debemos añadir una nueva llamada a `app.get` (o `app.post`, `app.put`, `app.delete`, etc.) con la URL deseada y la función que se ejecutará cuando se realice la HTTP request con el método indicado en esa URL. 65 | 66 | ```js 67 | app.get("/ruta", (req, res) => { 68 | // Esto envía el texto "Hello World!" como respuesta a la HTTP request 69 | res.send("Hello World!"); 70 | }); 71 | ``` 72 | 73 | Con la siguiente HTTP request se ejecutará la función asociada a la ruta `/ruta`: 74 | 75 | ```http 76 | GET http://host:port/ruta HTTP/1.1 77 | ``` 78 | 79 | ## Requests 80 | 81 | ### Métodos HTTP 82 | 83 | Los métodos HTTP más comunes son: 84 | 85 | - `GET`: Se utiliza para obtener información del servidor. Toda la información enviada en una petición GET es visible en la URL. 86 | - `POST`: Se utiliza para enviar información al servidor. La información enviada en una petición POST no es visible en la URL, se envía en el cuerpo (_body_) de la petición. 87 | - `PUT`: Se utiliza para actualizar información en el servidor. Se suele enviar información tanto en la URL como en el cuerpo de la petición. 88 | - `DELETE`: Se utiliza para eliminar información del servidor. Al igual que el método `GET`, la información a eliminar se suele enviar en la URL. 89 | 90 | #### GET 91 | 92 | La información enviada en una petición GET es visible en la URL. 93 | 94 | ##### Query parameters 95 | 96 | Una de las formas de envíar datos dentro de la URL, es utilzando los _query parameters_. 97 | 98 | Por ejemplo, si queremos enviar un parámetro `nombre` con el valor `Juan`, la URL sería `http://host:port/ruta?nombre=Juan`. 99 | 100 | Si quereos enviar más de un parámetro, simplemente debemos separarlos con el símbolo `&`. Por ejemplo, `http://host:port/ruta?nombre=Juan&apellido=Perez`. 101 | 102 | ```http 103 | GET http://host:port/ruta?nombre=Juan&apellido=Perez HTTP/1.1 104 | ``` 105 | 106 | Para obtener los valores de los _query parameters_ en el servidor, los encontramos en la propiedad `req.query`. 107 | 108 | ```js 109 | app.get("/ruta", (req, res) => { 110 | // Obtenemos el valor del parámetro "nombre" 111 | const nombre = req.query.nombre; 112 | // Obtenemos el valor del parámetro "apellido" 113 | const apellido = req.query.apellido; 114 | // Esto envía el texto "Hello !" como respuesta 115 | res.send(`Hello ${nombre} ${apellido}!`); 116 | }); 117 | ``` 118 | 119 | ##### Path parameters 120 | 121 | Otra forma de enviar datos dentro de la URL, es utilizando _path parameters_. 122 | 123 | Por ejemplo, si queremos enviar un parámetro `id` con el valor `123`, la URL sería `http://host:port/ruta/123`. 124 | 125 | Si quereos enviar más de un parámetro, simplemente debemos separarlos con el símbolo `/`. Por ejemplo, `http://host:port/ruta/123/Juan`. 126 | 127 | ```http 128 | GET http://host:port/ruta/123/Juan HTTP/1.1 129 | ``` 130 | 131 | Para obtener los valores de los _path parameters_ en el servidor. Debemos definir la ruta de una manera especial, indicando el nombre del parámetro precedido de `:`. El nombre del parámetro lo elegimos nosotros, y no afecta a la ruta en la URL. 132 | 133 | Cuando queremos recibir más de un parámetro, simplemente debemos añadir más `/:` seguidos del nombre del parámetro. Y los encontramos en la propiedad `req.params`. 134 | 135 | Es importante tener en cuenta que irán en el mismo orden en el que los hemos definido en la URL. 136 | 137 | ```js 138 | app.get("/ruta/:id/:nombre", (req, res) => { 139 | // Obtenemos el valor del parámetro "id" 140 | const id = req.params.id; 141 | // Obtenemos el valor del parámetro "nombre" 142 | const nombre = req.params.nombre; 143 | // Esto envía el texto "Hello with id !" como respuesta 144 | res.send(`Hello ${nombre} with id ${id}!`); 145 | }); 146 | ``` 147 | 148 | #### POST 149 | 150 | La información enviada en una petición POST no es visible en la URL, se envía en el cuerpo (_body_) de la petición. 151 | 152 | Cuando se crea un servidor en Express, debemos decidir cómo vamos a recibir la información enviada en una petición POST. En nuestro caso (para la materia Base de Datos), vamos a recibir la información en formato JSON, por lo que debemos añadir el siguiente _middleware_ al servidor: 153 | 154 | ```js 155 | app.use(express.json()); 156 | ``` 157 | 158 | Esto permite que Express pueda interpretar el cuerpo de la petición como un objeto JSON. 159 | 160 | **Importante**: Si no añadimos este _middleware_, el cuerpo de la petición estaría vacío, y no podremos acceder a los datos enviados en la petición POST. 161 | 162 | ##### Body parameters 163 | 164 | Para enviar información en una petición POST, debemos añadir un objeto JSON en el cuerpo de la petición. 165 | 166 | ```http 167 | POST http://host:port/ruta HTTP/1.1 168 | Content-Type: application/json 169 | 170 | { 171 | "nombre": "Juan", 172 | "apellido": "Perez" 173 | } 174 | ``` 175 | 176 | Para obtener los valores de los _body parameters_ en el servidor, los encontramos en la propiedad `req.body`. 177 | 178 | ```js 179 | app.post("/ruta", (req, res) => { 180 | // Obtenemos el valor del parámetro "nombre" 181 | const nombre = req.body.nombre; 182 | // Obtenemos el valor del parámetro "apellido" 183 | const apellido = req.body.apellido; 184 | // Esto envía el texto "Hello !" como respuesta 185 | res.send(`Hello ${nombre} ${apellido}!`); 186 | }); 187 | ``` 188 | 189 | #### PUT 190 | 191 | Este método es muy similar al método POST, pero se suele utilizar para actualizar información en el servidor. 192 | 193 | ```http 194 | PUT http://host:port/ruta/:id HTTP/1.1 195 | Content-Type: application/json 196 | 197 | { 198 | "nombre": "Juan" 199 | } 200 | ``` 201 | 202 | Para obtener los valores de los _body parameters_ en el servidor, los encontramos en la propiedad `req.body`. 203 | 204 | ```js 205 | app.put("/ruta/:id", (req, res) => { 206 | // Obtenemos el valor del parámetro "id" 207 | const id = req.params.id; 208 | // Obtenemos el valor del parámetro "nombre" 209 | const nombre = req.body.nombre; 210 | 211 | // Aquí iría la lógica para actualizar al recurso con el id indicado 212 | 213 | // Esto envía el texto "Hello with id !" como respuesta 214 | res.send(`Hello ${nombre} with id ${id}!`); 215 | }); 216 | ``` 217 | 218 | #### DELETE 219 | 220 | Este método es muy similar al método GET, pero se suele utilizar para eliminar información en lugar de obtenerla. 221 | 222 | ```http 223 | DELETE http://host:port/ruta/:id HTTP/1.1 224 | ``` 225 | 226 | Para obtener los valores de los _path parameters_ en el servidor. Los encontramos en la propiedad `req.params`. 227 | 228 | ```js 229 | app.delete("/ruta/:id", (req, res) => { 230 | // Obtenemos el valor del parámetro "id" 231 | const id = req.params.id; 232 | 233 | // Aquí iría la lógica para eliminar al recurso con el id indicado 234 | 235 | // Esto envía el texto "User with id deleted!" como respuesta 236 | res.send(`User with id ${id} deleted!`); 237 | }); 238 | ``` 239 | 240 | ## Responses 241 | 242 | A la hora de manejar las respuestas en nuestro servidor, Express utiliza una lógica que puede resultar confusa al principio, pero que es muy potente y flexible. 243 | 244 | La respuesta es primero enviada como parámetro a la función que se ejecuta cuando se realiza la petición. A partir de ahí, podemos utilizar diferentes métodos para modificar la respuesta antes de enviarla al cliente. 245 | 246 | ### Métodos de respuesta 247 | 248 | - `res.send(text)`: Envía una respuesta al cliente. El parámetro puede ser un objeto, un array, un string, un número, o `null`. 249 | - `res.json(data)`: Envía una respuesta JSON al cliente. El parámetro puede ser un objeto, un array, un string, un número, o `null`. 250 | - `res.status(status)`: Establece el código de estado de la respuesta.. 251 | - `res.sendStatus(status)`: Establece el código de estado de la respuesta y envía el texto asociado a ese código de estado. 252 | 253 | Por ejemplo, si queremos enviar un objeto JSON como respuesta a una petición GET, podemos hacerlo de la siguiente manera: 254 | 255 | ```js 256 | app.get("/ruta", (req, res) => { 257 | // Creamos un objeto JSON 258 | const data = { 259 | nombre: "Juan", 260 | apellido: "Perez" 261 | }; 262 | // Enviamos el objeto JSON como respuesta 263 | res.json(data); 264 | }); 265 | ``` 266 | --------------------------------------------------------------------------------