Selaa lähdekoodia

Login loading, errors y modal

wilitp 4 vuotta sitten
vanhempi
commit
a1aab249ea

+ 52 - 8
app/package-lock.json

@@ -8,9 +8,11 @@
       "name": "zuccardi-front",
       "version": "0.1.0",
       "dependencies": {
+        "@popperjs/core": "^2.10.2",
         "@testing-library/jest-dom": "^5.11.4",
         "@testing-library/react": "^11.1.0",
         "@testing-library/user-event": "^12.1.10",
+        "@types/bootstrap": "^5.1.6",
         "@types/jest": "^26.0.15",
         "@types/node": "^12.0.0",
         "@types/react": "^17.0.0",
@@ -2082,10 +2084,9 @@
       }
     },
     "node_modules/@popperjs/core": {
-      "version": "2.10.1",
-      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.10.1.tgz",
-      "integrity": "sha512-HnUhk1Sy9IuKrxEMdIRCxpIqPw6BFsbYSEUO9p/hNw5sMld/+3OLMWQP80F8/db9qsv3qUjs7ZR5bS/R+iinXw==",
-      "peer": true,
+      "version": "2.10.2",
+      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.10.2.tgz",
+      "integrity": "sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ==",
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/popperjs"
@@ -2503,6 +2504,15 @@
         "@babel/types": "^7.3.0"
       }
     },
+    "node_modules/@types/bootstrap": {
+      "version": "5.1.6",
+      "resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.1.6.tgz",
+      "integrity": "sha512-3L6IvOCKyoVd3e4bgQTH7VBPbuYEOG8IQbRcuZ0AbjfwPdRX+kVf5L/7mVt1EVM+D/BVw4+71rtp7Z8yYROlpQ==",
+      "dependencies": {
+        "@popperjs/core": "^2.9.2",
+        "@types/jquery": "*"
+      }
+    },
     "node_modules/@types/eslint": {
       "version": "7.2.6",
       "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.6.tgz",
@@ -2574,6 +2584,14 @@
         "pretty-format": "^26.0.0"
       }
     },
+    "node_modules/@types/jquery": {
+      "version": "3.5.8",
+      "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.8.tgz",
+      "integrity": "sha512-cXk6NwqjDYg+UI9p2l3x0YmPa4m7RrXqmbK4IpVVpRJiYXU/QTo+UZrn54qfE1+9Gao4qpYqUnxm5ZCy2FTXAw==",
+      "dependencies": {
+        "@types/sizzle": "*"
+      }
+    },
     "node_modules/@types/json-schema": {
       "version": "7.0.7",
       "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
@@ -2669,6 +2687,11 @@
       "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
       "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
     },
+    "node_modules/@types/sizzle": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz",
+      "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ=="
+    },
     "node_modules/@types/source-list-map": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
@@ -21589,10 +21612,9 @@
       }
     },
     "@popperjs/core": {
-      "version": "2.10.1",
-      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.10.1.tgz",
-      "integrity": "sha512-HnUhk1Sy9IuKrxEMdIRCxpIqPw6BFsbYSEUO9p/hNw5sMld/+3OLMWQP80F8/db9qsv3qUjs7ZR5bS/R+iinXw==",
-      "peer": true
+      "version": "2.10.2",
+      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.10.2.tgz",
+      "integrity": "sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ=="
     },
     "@rollup/plugin-node-resolve": {
       "version": "7.1.3",
@@ -21872,6 +21894,15 @@
         "@babel/types": "^7.3.0"
       }
     },
+    "@types/bootstrap": {
+      "version": "5.1.6",
+      "resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.1.6.tgz",
+      "integrity": "sha512-3L6IvOCKyoVd3e4bgQTH7VBPbuYEOG8IQbRcuZ0AbjfwPdRX+kVf5L/7mVt1EVM+D/BVw4+71rtp7Z8yYROlpQ==",
+      "requires": {
+        "@popperjs/core": "^2.9.2",
+        "@types/jquery": "*"
+      }
+    },
     "@types/eslint": {
       "version": "7.2.6",
       "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.6.tgz",
@@ -21943,6 +21974,14 @@
         "pretty-format": "^26.0.0"
       }
     },
+    "@types/jquery": {
+      "version": "3.5.8",
+      "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.8.tgz",
+      "integrity": "sha512-cXk6NwqjDYg+UI9p2l3x0YmPa4m7RrXqmbK4IpVVpRJiYXU/QTo+UZrn54qfE1+9Gao4qpYqUnxm5ZCy2FTXAw==",
+      "requires": {
+        "@types/sizzle": "*"
+      }
+    },
     "@types/json-schema": {
       "version": "7.0.7",
       "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
@@ -22038,6 +22077,11 @@
       "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
       "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
     },
+    "@types/sizzle": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz",
+      "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ=="
+    },
     "@types/source-list-map": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",

+ 2 - 0
app/package.json

@@ -3,9 +3,11 @@
   "version": "0.1.0",
   "private": true,
   "dependencies": {
+    "@popperjs/core": "^2.10.2",
     "@testing-library/jest-dom": "^5.11.4",
     "@testing-library/react": "^11.1.0",
     "@testing-library/user-event": "^12.1.10",
+    "@types/bootstrap": "^5.1.6",
     "@types/jest": "^26.0.15",
     "@types/node": "^12.0.0",
     "@types/react": "^17.0.0",

+ 6 - 11
app/public/index.html

@@ -27,17 +27,12 @@
     <title>React App</title>
   </head>
   <body>
-    <noscript>You need to enable JavaScript to run this app.</noscript>
+    <noscript
+      >Necesitas tener javascript activado para usar esta aplicacion.</noscript
+    >
     <div id="root"></div>
-    <!--
-      This HTML file is a template.
-      If you open it directly in the browser, you will see an empty page.
-
-      You can add webfonts, meta tags, or analytics to this file.
-      The build step will place the bundled scripts into the <body> tag.
-
-      To begin the development, run `npm start` or `yarn start`.
-      To create a production bundle, use `npm run build` or `yarn build`.
-    -->
+    <aside class="modal" id="page-modal">
+      <div class="modal-dialog-centered modal-dialog" id="modal-portal"></div>
+    </aside>
   </body>
 </html>

+ 25 - 2
app/src/components/pages/Login.tsx

@@ -10,7 +10,8 @@ import {
 } from "../../context/auth/AuthProvider";
 import { Redirect } from "react-router-dom";
 import * as classes from "./Login.module.css";
-import { loginRequest } from "../../context/auth/actions";
+import { failureDismiss, login } from "../../context/auth/actions";
+import Modal from "../portals/modal";
 
 const Login = () => {
   const [formState, setFormState] = useState<any>({});
@@ -29,15 +30,37 @@ const Login = () => {
 
   const handleFormSubmit: FormEventHandler = (e) => {
     e.preventDefault();
-    dispatch(loginRequest(formState.username, formState.password));
+    dispatch(login(formState.username, formState.password));
   };
 
   if (userState.loggedIn) {
     return <Redirect to="/" />;
   }
 
+  // Modal
+  let modal = null;
+  if (userState.loggingIn) {
+    modal = (
+      <Modal key="Loading" buttons={false}>
+        <p>Espere un momento</p>
+      </Modal>
+    );
+  } else if (userState.error) {
+    modal = (
+      <Modal
+        key="Error"
+        title={"Error"}
+        staticBackdrop
+        accept={() => dispatch(failureDismiss())}
+      >
+        <p>{userState.error}</p>
+      </Modal>
+    );
+  }
+
   return (
     <main className="d-flex justify-content-center align-items-center min-vh-100">
+      {modal}
       <form
         onSubmit={handleFormSubmit}
         className={`text-center d-flex flex-column gap-2  ${classes.loginForm}`}

+ 92 - 0
app/src/components/portals/modal/index.tsx

@@ -0,0 +1,92 @@
+import React, { FC, useState, useEffect, MouseEventHandler } from "react";
+import { createPortal } from "react-dom";
+import { Modal as BsModal } from "bootstrap";
+
+// Este componente es un react portal. No se va a montar adentro de el componente donde lo uses.
+// Cuando lo uses, va a montarse siempre en el mismo lugar, pero tenes acceso a todos sus eventos de todas formas.
+const Modal: FC<ModalProps> = ({
+  children,
+  accept,
+  dismiss,
+  acceptText = "Aceptar",
+  dismissText = "Cancelar",
+  title,
+  buttons = true,
+  staticBackdrop = false,
+}) => {
+  // Mounting point
+  const root = document.getElementById("modal-portal");
+  const [bsModal, setBsModal] = useState<BsModal | null>(null);
+
+  useEffect(() => {
+    const modalEl = document.getElementById("page-modal");
+    let modalTemp: null | BsModal = null;
+    if (!modalEl) return;
+    console.log("Effect run", { staticBackdrop });
+    if (staticBackdrop) {
+      console.log({ modalEl });
+      modalEl.setAttribute("data-bs-backdrop", "static");
+    }
+    if (!bsModal) {
+      modalTemp = new BsModal(modalEl);
+      setBsModal(modalTemp);
+    }
+    modalTemp?.show();
+
+    return () => {
+      modalTemp?.hide();
+      modalEl?.removeAttribute("data-bs-backdrop");
+    };
+  }, []);
+
+  const content = (
+    <div className="modal-content">
+      {title ? (
+        <div className="modal-header">
+          <h5 className="modal-title">{title}</h5>
+          <button
+            type="button"
+            className="btn-close"
+            data-bs-dismiss="modal"
+            aria-label="Close"
+          ></button>
+        </div>
+      ) : null}
+      <div className="modal-body">{children}</div>
+      {buttons && (dismiss || accept) && (
+        <div className="modal-footer">
+          {dismiss && (
+            <button
+              type="button"
+              className="btn btn-secondary"
+              data-bs-dismiss="modal"
+              onClick={dismiss}
+            >
+              {dismissText}
+            </button>
+          )}
+          {accept && (
+            <button type="button" onClick={accept} className="btn btn-primary">
+              {acceptText}
+            </button>
+          )}
+        </div>
+      )}
+    </div>
+  );
+
+  if (!root) return null;
+  return createPortal(content, root);
+};
+
+interface ModalProps {
+  title?: string;
+  buttons?: boolean;
+  dismiss?: MouseEventHandler<HTMLButtonElement>;
+  accept?: MouseEventHandler<HTMLButtonElement>;
+  dismissText?: string;
+  acceptText?: string;
+  staticBackdrop?: boolean;
+}
+
+export default Modal;

+ 2 - 0
app/src/components/portals/modal/modal.module.css

@@ -0,0 +1,2 @@
+.modal {
+}

+ 24 - 4
app/yarn.lock

@@ -1409,10 +1409,10 @@
     "schema-utils" "^2.6.5"
     "source-map" "^0.7.3"
 
-"@popperjs/core@^2.10.1":
-  "integrity" "sha512-HnUhk1Sy9IuKrxEMdIRCxpIqPw6BFsbYSEUO9p/hNw5sMld/+3OLMWQP80F8/db9qsv3qUjs7ZR5bS/R+iinXw=="
-  "resolved" "https://registry.npmjs.org/@popperjs/core/-/core-2.10.1.tgz"
-  "version" "2.10.1"
+"@popperjs/core@^2.10.1", "@popperjs/core@^2.10.2", "@popperjs/core@^2.9.2":
+  "integrity" "sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ=="
+  "resolved" "https://registry.npmjs.org/@popperjs/core/-/core-2.10.2.tgz"
+  "version" "2.10.2"
 
 "@rollup/plugin-node-resolve@^7.1.1":
   "integrity" "sha512-RxtSL3XmdTAE2byxekYLnx+98kEUOrPHF/KRVjLH+DEIHy6kjIw7YINQzn+NXiH/NTrQLAwYs0GWB+csWygA9Q=="
@@ -1654,6 +1654,14 @@
   dependencies:
     "@babel/types" "^7.3.0"
 
+"@types/bootstrap@^5.1.6":
+  "integrity" "sha512-3L6IvOCKyoVd3e4bgQTH7VBPbuYEOG8IQbRcuZ0AbjfwPdRX+kVf5L/7mVt1EVM+D/BVw4+71rtp7Z8yYROlpQ=="
+  "resolved" "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.1.6.tgz"
+  "version" "5.1.6"
+  dependencies:
+    "@popperjs/core" "^2.9.2"
+    "@types/jquery" "*"
+
 "@types/eslint@^7.2.6":
   "integrity" "sha512-I+1sYH+NPQ3/tVqCeUSBwTE/0heyvtXqpIopUUArlBm0Kpocb8FbMa3AZ/ASKIFpN3rnEx932TTXDbt9OXsNDw=="
   "resolved" "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.6.tgz"
@@ -1724,6 +1732,13 @@
     "jest-diff" "^26.0.0"
     "pretty-format" "^26.0.0"
 
+"@types/jquery@*":
+  "integrity" "sha512-cXk6NwqjDYg+UI9p2l3x0YmPa4m7RrXqmbK4IpVVpRJiYXU/QTo+UZrn54qfE1+9Gao4qpYqUnxm5ZCy2FTXAw=="
+  "resolved" "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.8.tgz"
+  "version" "3.5.8"
+  dependencies:
+    "@types/sizzle" "*"
+
 "@types/json-schema@*", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6":
   "integrity" "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA=="
   "resolved" "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz"
@@ -1814,6 +1829,11 @@
   "resolved" "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz"
   "version" "0.16.2"
 
+"@types/sizzle@*":
+  "integrity" "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ=="
+  "resolved" "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz"
+  "version" "2.3.3"
+
 "@types/source-list-map@*":
   "integrity" "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA=="
   "resolved" "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz"

+ 0 - 6
package-lock.json

@@ -1,6 +0,0 @@
-{
-  "name": "zuccardi-front",
-  "lockfileVersion": 2,
-  "requires": true,
-  "packages": {}
-}