Bladeren bron

Merge branch 'login' into develop

wilitp 4 jaren geleden
bovenliggende
commit
61676a1bfc

+ 58 - 54
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",
@@ -4127,15 +4150,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/bindings": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
-      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
-      "optional": true,
-      "dependencies": {
-        "file-uri-to-path": "1.0.0"
-      }
-    },
     "node_modules/bluebird": {
       "version": "3.7.2",
       "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
@@ -7725,12 +7739,6 @@
         "url": "https://opencollective.com/webpack"
       }
     },
-    "node_modules/file-uri-to-path": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
-      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
-      "optional": true
-    },
     "node_modules/filesize": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz",
@@ -7913,9 +7921,9 @@
       }
     },
     "node_modules/follow-redirects": {
-      "version": "1.13.2",
-      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.2.tgz",
-      "integrity": "sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA==",
+      "version": "1.14.4",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz",
+      "integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==",
       "funding": [
         {
           "type": "individual",
@@ -11642,12 +11650,6 @@
       "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
       "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE="
     },
-    "node_modules/nan": {
-      "version": "2.15.0",
-      "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
-      "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
-      "optional": true
-    },
     "node_modules/nanoid": {
       "version": "3.1.20",
       "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz",
@@ -21610,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",
@@ -21893,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",
@@ -21964,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",
@@ -22059,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",
@@ -23213,14 +23236,6 @@
       "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
       "optional": true
     },
-    "bindings": {
-      "version": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
-      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
-      "optional": true,
-      "requires": {
-        "file-uri-to-path": "1.0.0"
-      }
-    },
     "bluebird": {
       "version": "3.7.2",
       "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
@@ -26078,12 +26093,6 @@
         }
       }
     },
-    "file-uri-to-path": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
-      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
-      "optional": true
-    },
     "filesize": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz",
@@ -26238,9 +26247,9 @@
       }
     },
     "follow-redirects": {
-      "version": "1.13.2",
-      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.2.tgz",
-      "integrity": "sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA=="
+      "version": "1.14.4",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz",
+      "integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g=="
     },
     "for-in": {
       "version": "1.0.2",
@@ -29126,11 +29135,6 @@
       "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
       "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE="
     },
-    "nan": {
-      "version": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
-      "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
-      "optional": true
-    },
     "nanoid": {
       "version": "3.1.20",
       "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.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>

+ 8 - 4
app/src/App.tsx

@@ -1,18 +1,22 @@
 import React from "react";
 import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
-import Layout from "./components/layout";
-import Home from "./components/pages/home";
+import Home from "./components/pages/Home";
+import Login from "./components/pages/Login";
+import AuthProvider from "./context/auth/AuthProvider";
 
 function App() {
   return (
     <Router>
-      <Layout>
+      <AuthProvider>
         <Switch>
           <Route exact path="/">
             <Home />
           </Route>
+          <Route exact path="/login">
+            <Login />
+          </Route>
         </Switch>
-      </Layout>
+      </AuthProvider>
     </Router>
   );
 }

+ 22 - 0
app/src/api/auth.ts

@@ -0,0 +1,22 @@
+export function login(username: string, password: string) {
+  const headers = new Headers();
+  headers.append("Content-Type", "application/x-www-form-urlencoded");
+  headers.append("Access-Control-Allow-Origin", "*");
+  headers.append(
+    "Cookie",
+    "csrftoken=H9Vl6eq4nSBWr9UjJ5KfoH1L0HSYV745xy65G9e5FumSt9eZo2ODpmRua9v4N8Si"
+  );
+
+  const urlencoded = new URLSearchParams();
+  urlencoded.append("username", username);
+  urlencoded.append("password", password);
+
+  const requestOptions: RequestInit = {
+    method: "POST",
+    headers: headers,
+    body: urlencoded,
+    mode: "cors",
+    redirect: "follow",
+  };
+  return fetch(`http://localhost:8000/v1/login`, requestOptions);
+}

+ 1 - 0
app/src/api/index.ts

@@ -0,0 +1 @@
+export * from "./auth";

+ 24 - 0
app/src/components/UI/forms/calendarInput.tsx

@@ -0,0 +1,24 @@
+import React, { FC, useState } from "react";
+
+interface calendarInputProps {
+    onChange: Function;
+    name: string;
+    value?: string;
+    min?: string;
+    max?: string;
+    [index: string]: any;
+}
+
+const CalendarInput: FC<calendarInputProps> = ({ className, ...props }) => {
+
+    const [startDate, setStartDate] = useState(new Date());
+
+    return (
+        <input {...(props as any)}
+            defaultValue={startDate.toISOString().substr(0, 10)}
+            className={`form-control ${className}`}
+            type="date" id="start" />
+    );
+};
+
+export default CalendarInput;

+ 3 - 3
app/src/components/UI/forms/select.tsx

@@ -1,7 +1,7 @@
 import React, { FC } from "react";
 
 interface selectProps {
-  list: string[] | number[];
+  list?: string[] | number[];
   onChange: Function;
   placeholder: string;
   name: string;
@@ -11,10 +11,10 @@ interface selectProps {
 // En las props hago un "rest", que me da el resto del objeto que estoy desestructurando
 const Select: FC<selectProps> = ({ list, className, ...props }) => {
   return (
-    <select {...(props as any)} className={`form-select ${className}`}>
+    <select {...(props as any)}  className={`form-select ${className}`}>
       {props.placeholder ? <option value="">{props.placeholder}</option> : null}
 
-      {list.map((item) => (
+      {list?.map((item) => (
         <option value={item} key={item}>
           {item}
         </option>

+ 54 - 0
app/src/components/data/DegreeDay/index.tsx

@@ -0,0 +1,54 @@
+import React, { FC } from "react";
+import * as classes from "../tables.module.css";
+
+interface DegreeDayProps {
+  title: String;
+  periodString: String;
+}
+
+const DegreeDay: FC<DegreeDayProps> = ({ title, periodString }) => {
+  const suffix = "Precipitaciones mensuales";
+  return (
+    <section>
+      <h3>{`${title} - ${suffix}`}</h3>
+
+      <table className={classes.table}>
+        <thead>
+          <tr>
+            <th className={classes.cell}>{periodString}</th>
+            <td className={classes.cell}>Octubre</td>
+            <td className={classes.cell}>Noviembre</td>
+            <td className={classes.cell}>Diciembre</td>
+            <td className={classes.cell}>Enero</td>
+            <td className={classes.cell}>Febrero</td>
+            <td className={classes.cell}>Marzo</td>
+            <td className={classes.cell}>Precipitaciones acumuladas [mm]</td>
+          </tr>
+        </thead>
+        <tbody>
+          <tr>
+            <th className={classes.cell}>Temporada 2020-2021-Vendimia 2021</th>
+            <td className={classes.cell}>4,44</td>
+            <td className={classes.cell}>5,02</td>
+            <td className={classes.cell}>11,02</td>
+            <td className={classes.cell}>14,02</td>
+            <td className={classes.cell}>12,02</td>
+            <td className={classes.cell}>8,02</td>
+            <td className={classes.cell}>205</td>
+          </tr>
+          <tr>
+            <th className={classes.cell}>Temporada 2020-2021-Vendimia 2021</th>
+            <td className={classes.cell}>4,44</td>
+            <td className={classes.cell}>5,02</td>
+            <td className={classes.cell}>11,02</td>
+            <td className={classes.cell}>14,02</td>
+            <td className={classes.cell}>12,02</td>
+            <td className={classes.cell}>8,02</td>
+            <td className={classes.cell}>440</td>
+          </tr>
+        </tbody>
+      </table>
+    </section>
+  );
+};
+export default DegreeDay;

+ 6 - 0
app/src/components/data/HeatMap/index.tsx

@@ -0,0 +1,6 @@
+import React, { FC } from "react";
+
+const HeatMap: FC = () => {
+  return null;
+};
+export default HeatMap;

+ 51 - 0
app/src/components/data/Precipitation/index.tsx

@@ -0,0 +1,51 @@
+import React, { FC } from "react";
+import * as classes from "../tables.module.css";
+
+interface PrecipitationProps {
+  title: String;
+  periodString: String;
+}
+
+const Precipitation: FC<PrecipitationProps> = ({ title, periodString }) => {
+  const suffix = "Grados días promedios mensuales";
+  return (
+    <section>
+      <h3>{`${title} - ${suffix}`}</h3>
+
+      <table className={classes.table}>
+        <thead>
+          <tr>
+            <th className={classes.cell}>{periodString}</th>
+            <td className={classes.cell}>Octubre</td>
+            <td className={classes.cell}>Noviembre</td>
+            <td className={classes.cell}>Diciembre</td>
+            <td className={classes.cell}>Enero</td>
+            <td className={classes.cell}>Febrero</td>
+            <td className={classes.cell}>Marzo</td>
+          </tr>
+        </thead>
+        <tbody>
+          <tr>
+            <th className={classes.cell}>Temporada 2020-2021-Vendimia 2021</th>
+            <td className={classes.cell}>4,44</td>
+            <td className={classes.cell}>5,02</td>
+            <td className={classes.cell}>11,02</td>
+            <td className={classes.cell}>14,02</td>
+            <td className={classes.cell}>12,02</td>
+            <td className={classes.cell}>8,02</td>
+          </tr>
+          <tr>
+            <th className={classes.cell}>Temporada 2020-2021-Vendimia 2021</th>
+            <td className={classes.cell}>4,44</td>
+            <td className={classes.cell}>5,02</td>
+            <td className={classes.cell}>11,02</td>
+            <td className={classes.cell}>14,02</td>
+            <td className={classes.cell}>12,02</td>
+            <td className={classes.cell}>8,02</td>
+          </tr>
+        </tbody>
+      </table>
+    </section>
+  );
+};
+export default Precipitation;

+ 50 - 0
app/src/components/data/TemperaturePerSeason/index.tsx

@@ -0,0 +1,50 @@
+import React, { FC } from "react";
+import { TableHeader as Header, TdGroup } from "../shared";
+import * as classes from "../tables.module.css";
+
+const TemperaturePerSeason: FC = () => {
+  return (
+    <table className={classes.table}>
+      <Header daysToMatchCurrentTemperature={true} />
+      <tbody>
+        <tr>
+          <th className={classes.cell}>Temporada 2015-2016 - Vendimia 2016</th>
+          <TdGroup>
+            <td>4,51%</td>
+            <td>17,86%</td>
+            <td>8,84%</td>
+          </TdGroup>
+          <td className={classes.cell}>2215</td>
+          <td className={classes.cell}>(+)10 días</td>
+          <td className={classes.cell}>15,1</td>
+          <td className={classes.cell}>357</td>
+        </tr>
+        <tr>
+          <th className={classes.cell}>Temporada 2016-2017 - Vendimia 2017</th>
+          <TdGroup>
+            <td>4,51%</td>
+            <td>17,86%</td>
+            <td>8,84%</td>
+          </TdGroup>
+          <td className={classes.cell}>2215</td>
+          <td className={classes.cell}>(+)10 días</td>
+          <td className={classes.cell}>15,1</td>
+          <td className={classes.cell}>357</td>
+        </tr>
+        <tr>
+          <th className={classes.cell}>Temporada 2016-2017 - Vendimia 2017</th>
+          <TdGroup>
+            <td>4,51%</td>
+            <td>17,86%</td>
+            <td>8,84%</td>
+          </TdGroup>
+          <td className={classes.cell}>2215</td>
+          <td className={classes.cell}>(+)10 días</td>
+          <td className={classes.cell}>15,1</td>
+          <td className={classes.cell}>357</td>
+        </tr>
+      </tbody>
+    </table>
+  );
+};
+export default TemperaturePerSeason;

+ 92 - 0
app/src/components/data/TemperaturePerSector/index.tsx

@@ -0,0 +1,92 @@
+import React, { FC } from "react";
+import { TableHeader as Header, TdGroup } from "../shared";
+import * as classes from "../tables.module.css";
+
+const TemperaturePerSector: FC = () => {
+  return (
+    <table className={classes.table}>
+      <Header daysToMatchCurrentTemperature={false}/>
+      <tbody>
+        {/* Esto va a ser dinamico */}
+        <tr>
+          <th className={classes.cell}>Paraje Altamira</th>
+          <TdGroup>
+            <td>8,58%</td>
+            <td>7,28%</td>
+            <td>1,72%</td>
+          </TdGroup>
+          <td className={classes.cell}>16%</td>
+          <td className={classes.cell}>16.0</td>
+          <td className={classes.cell}>523</td>
+        </tr>
+        <tr>
+          <th className={classes.cell}>San Pablo</th>
+          <TdGroup>
+            <td>8,58%</td>
+            <td>7,28%</td>
+            <td>1,72%</td>
+          </TdGroup>
+          <td className={classes.cell}>16%</td>
+          <td className={classes.cell}>16.0</td>
+          <td className={classes.cell}>523</td>
+        </tr>
+        <tr>
+          <th className={classes.cell}>Gualtallary</th>
+          <TdGroup>
+            <td>8,58%</td>
+            <td>7,28%</td>
+            <td>1,72%</td>
+          </TdGroup>
+          <td className={classes.cell}>16%</td>
+          <td className={classes.cell}>16.0</td>
+          <td className={classes.cell}>523</td>
+        </tr>
+        <tr>
+          <th className={classes.cell}>Vista Flores</th>
+          <TdGroup>
+            <td>8,58%</td>
+            <td>7,28%</td>
+            <td>1,72%</td>
+          </TdGroup>
+          <td className={classes.cell}>16%</td>
+          <td className={classes.cell}>16.0</td>
+          <td className={classes.cell}>523</td>
+        </tr>
+        <tr>
+          <th className={classes.cell}>La Ribera</th>
+          <TdGroup>
+            <td>8,58%</td>
+            <td>7,28%</td>
+            <td>1,72%</td>
+          </TdGroup>
+          <td className={classes.cell}>16%</td>
+          <td className={classes.cell}>16.0</td>
+          <td className={classes.cell}>523</td>
+        </tr>
+        <tr>
+          <th className={classes.cell}>Maipú</th>
+          <TdGroup>
+            <td>8,58%</td>
+            <td>7,28%</td>
+            <td>1,72%</td>
+          </TdGroup>
+          <td className={classes.cell}>16%</td>
+          <td className={classes.cell}>16.0</td>
+          <td className={classes.cell}>523</td>
+        </tr>
+        <tr>
+          <th className={classes.cell}>Santa Rosa</th>
+          <TdGroup>
+            <td>8,58%</td>
+            <td>7,28%</td>
+            <td>1,72%</td>
+          </TdGroup>
+          <td className={classes.cell}>16%</td>
+          <td className={classes.cell}>16.0</td>
+          <td className={classes.cell}>523</td>
+        </tr>
+      </tbody>
+    </table>
+  );
+};
+export default TemperaturePerSector;

+ 47 - 0
app/src/components/data/shared.tsx

@@ -0,0 +1,47 @@
+import React, { FC } from "react";
+import * as classes from "./tables.module.css";
+
+interface tableHeaderProps {
+  //Change this name
+  daysToMatchCurrentTemperature: boolean;
+}
+
+export const TableHeader: FC<tableHeaderProps> = ({ daysToMatchCurrentTemperature }) => {
+  return (
+    <thead>
+      <th className={classes.cell}>Octubre - Marzo</th>
+      <td style={{ padding: "0" }} className={classes.cell}>
+        <table style={{ width: "100%" }}>
+          <tr>
+            <td colSpan={100}>% Horas segun temperatura</td>
+          </tr>
+          <tr>
+            <td>&lt;10%</td>
+            <td>&gt;30%</td>
+            <td>&gt;33%</td>
+          </tr>
+        </table>
+      </td>
+
+      <td className={classes.cell}>Grados días acumulados</td>
+
+      {daysToMatchCurrentTemperature &&
+        <td className={classes.cell}>Días para igualar temperatura actual</td>
+      }
+
+      <td className={classes.cell}>Amplitud térmica</td>
+
+      <td className={classes.cell}>Precipitaciones acumuladas [mm]</td>
+    </thead>
+  );
+};
+
+export const TdGroup: FC = ({ children }) => {
+  return (
+    <td className={classes.tdGroup}>
+      <table>
+        <tr>{children}</tr>
+      </table>
+    </td>
+  );
+};

+ 23 - 0
app/src/components/data/tables.module.css

@@ -0,0 +1,23 @@
+.table {
+  /* Esto es para que las tablas nesteadas tengan ancho y alto 100%
+   * No tengo idea de por que funciona
+   * */
+  height: 1px;
+}
+
+.table * {
+  text-align: center;
+}
+
+.tdGroup {
+  border: 1px solid rgba(0, 0, 0, 0.05);
+}
+.tdGroup table {
+  height: 100%;
+  width: 100%;
+}
+
+.cell {
+  padding: 5px 10px;
+  border: 1px solid rgba(0, 0, 0, 0.05);
+}

+ 1 - 1
app/src/components/layout/index.tsx

@@ -14,7 +14,7 @@ const Layout = ({ children }: any) => {
   return (
     <>
       <Header toggle={toggle} />
-      <div className="container-fluid mx-auto px-0 flex-norwap row">
+      <div className="container-fluid mx-auto px-0 row flex-nowrap">
         <section
           className={`sidebar bg-light col-auto min-vh-100 col-md-3 col-xl-2 ${sbClass}`}
         >

+ 10 - 1
app/src/components/layout/sidebar.tsx

@@ -1,5 +1,7 @@
-import React from "react";
+import React, { useContext } from "react";
 import { Link } from "react-router-dom";
+import { logout } from "../../context/auth/actions";
+import { UserDispatchContext } from "../../context/auth/AuthProvider";
 
 const NavLink = ({ children, ...props }: any) => (
   <Link className="nav-link" {...props}>
@@ -8,6 +10,8 @@ const NavLink = ({ children, ...props }: any) => (
 );
 
 const Sidebar = () => {
+  const dispatch = useContext(UserDispatchContext);
+
   return (
     <aside className="pt-5">
       <ul className="nav nav-pills flex-column">
@@ -20,6 +24,11 @@ const Sidebar = () => {
         <li>
           <NavLink to="/mapa-calor">Mapa de Calor</NavLink>
         </li>
+        <li>
+          <button className="nav-link" onClick={() => dispatch(logout())}>
+            Logout
+          </button>
+        </li>
       </ul>
     </aside>
   );

+ 91 - 0
app/src/components/pages/Home.tsx

@@ -0,0 +1,91 @@
+import React, { useContext, ChangeEvent, FC } from "react";
+import Select from "../UI/forms/select";
+import DegreeDay from "../data/DegreeDay";
+import TemperaturePerSeason from "../data/TemperaturePerSeason";
+import TemperaturePerSector from "../data/TemperaturePerSector";
+import Precipitation from "../data/Precipitation";
+import CalendarInput from "../UI/forms/calendarInput";
+import { UserStateContext } from "../../context/auth/AuthProvider";
+import { Redirect } from "react-router-dom";
+import Layout from "../layout";
+
+const Home: FC = () => {
+  let fincaList: string[] | number[] = ["a", "b", "c", "d"];
+  let campaignList: string[] | number[] = ["2018", "2019", "2020", "2021"];
+
+  const userState = useContext(UserStateContext);
+
+  if (!userState.loggedIn) {
+    return <Redirect to="/login" />;
+  }
+
+  return (
+    <>
+      <Layout>
+        <section className="row p-lg-4 p-md-3 p-2">
+          <div className="col-12 col-lg-4 mb-2 col-xl-3 mb-xl-0">
+            <Select
+              list={fincaList}
+              onChange={(e: ChangeEvent<HTMLInputElement>) =>
+                console.log(e.target.value)
+              }
+              name="Comparación"
+              placeholder="Fincas"
+            />
+          </div>
+          <div className="col-6 col-lg-4 mb-2 col-xl-auto mb-xl-0">
+            <CalendarInput
+              onChange={(e: ChangeEvent<HTMLInputElement>) =>
+                console.log(e.target.value)
+              }
+              name="Comparación"
+            />
+          </div>
+          <div className="col-6 col-lg-4 mb-2 col-xl-auto mb-xl-0">
+            <CalendarInput
+              onChange={(e: ChangeEvent<HTMLInputElement>) =>
+                console.log(e.target.value)
+              }
+              name="Comparación"
+            />
+          </div>
+          <div className="col-6 col-lg-4 mb-2 col-xl-auto mb-xl-0">
+            <Select
+              list={campaignList}
+              onChange={(e: ChangeEvent<HTMLInputElement>) =>
+                console.log(e.target.value)
+              }
+              name="Comparación"
+              placeholder="Año historicos"
+            />
+          </div>
+
+          <div className="col-auto">
+            <button type="button" className="btn btn-primary">
+              Aplicar
+            </button>
+          </div>
+        </section>
+
+        {/* <section className="row"> */}
+        {/*   <div className="col-xl-6"> */}
+        {/*     <TemperaturePerSeason /> */}
+        {/*     <DegreeDay */}
+        {/*       title={"Vista flores"} */}
+        {/*       periodString={"1ro Octubre - 31 Marzo"} */}
+        {/*     /> */}
+        {/*     <Precipitation */}
+        {/*       title={"Maipú"} */}
+        {/*       periodString={"1ro Octubre - 31 Marzo"} */}
+        {/*     /> */}
+        {/*   </div> */}
+        {/*   <div className="col-xl-6"> */}
+        {/*     <TemperaturePerSector /> */}
+        {/*   </div> */}
+        {/* </section> */}
+      </Layout>
+    </>
+  );
+};
+
+export default Home;

+ 5 - 0
app/src/components/pages/Login.module.css

@@ -0,0 +1,5 @@
+.loginForm {
+  width: 100%;
+  max-width: 400px;
+  padding: 0 5px;
+}

+ 93 - 0
app/src/components/pages/Login.tsx

@@ -0,0 +1,93 @@
+import React, {
+  ChangeEvent,
+  useContext,
+  FormEventHandler,
+  useState,
+} from "react";
+import {
+  UserDispatchContext,
+  UserStateContext,
+} from "../../context/auth/AuthProvider";
+import { Redirect } from "react-router-dom";
+import * as classes from "./Login.module.css";
+import { failureDismiss, login } from "../../context/auth/actions";
+import Modal from "../portals/modal";
+
+const Login = () => {
+  const [formState, setFormState] = useState<any>({});
+  const dispatch = useContext(UserDispatchContext);
+  const userState = useContext(UserStateContext);
+
+  const handleFormChange = (e: ChangeEvent) => {
+    const el = e.target as HTMLInputElement;
+    const name = el.getAttribute("name") as string;
+    const value = el.value;
+    setFormState({
+      ...formState,
+      [name]: value,
+    });
+  };
+
+  const handleFormSubmit: FormEventHandler = (e) => {
+    e.preventDefault();
+    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}`}
+      >
+        <h3>Ingresar</h3>
+        <input
+          onChange={handleFormChange}
+          id="username"
+          className="form-control"
+          type="text"
+          name="username"
+          placeholder="Usuario"
+        />
+        <input
+          onChange={handleFormChange}
+          id="password"
+          className="form-control"
+          type="password"
+          name="password"
+          placeholder="Contraseña"
+        />
+        <button className="btn btn-primary" type="submit">
+          Continuar
+        </button>
+      </form>
+    </main>
+  );
+};
+
+export default Login;

+ 0 - 7
app/src/components/pages/home.tsx

@@ -1,7 +0,0 @@
-import React, { FC } from "react";
-
-const Home: FC = () => {
-  return <h1>Pagina Inicio</h1>;
-};
-
-export default Home;

+ 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 {
+}

+ 38 - 0
app/src/context/auth/AuthProvider.tsx

@@ -0,0 +1,38 @@
+import React, { createContext, useReducer, Dispatch, FC } from "react";
+import reducer, { State, getInitialState } from "./reducer";
+import { Action } from "./actionTypes";
+
+const UserProvider: FC = ({ children }) => {
+  const [state, dispatch] = useReducer(reducer, getInitialState());
+
+  return (
+    <UserStateContext.Provider value={state}>
+      <UserDispatchContext.Provider value={asyncDispatchWrap(dispatch)}>
+        {children}
+      </UserDispatchContext.Provider>
+    </UserStateContext.Provider>
+  );
+};
+
+type AsyncDispatch = (
+  action: Action | ((...args: any) => Promise<any>)
+) => void;
+
+function asyncDispatchWrap(dispatch: Dispatch<Action>) {
+  const asyncDispatch: AsyncDispatch = (action) => {
+    if (action instanceof Function) {
+      action(dispatch);
+      return;
+    }
+    dispatch(action);
+  };
+
+  return asyncDispatch;
+}
+
+export const UserStateContext = createContext<State>({});
+export const UserDispatchContext = createContext<
+  (action: Action | ((...args: any) => Promise<any>)) => void
+>(asyncDispatchWrap((dispatch) => {}));
+
+export default UserProvider;

+ 20 - 0
app/src/context/auth/actionTypes.ts

@@ -0,0 +1,20 @@
+// Idealmente estas constantes NO deberían usarse.
+// Deberíamos preferir usar los tipos de abajo.
+export const LOGIN_REQUEST = "LOGIN_REQUEST";
+export const LOGIN_SUCCESS = "LOGIN_SUCCESS";
+export const LOGIN_FAILURE = "LOGIN_FAILURE";
+export const LOGOUT = "LOGOUT";
+
+export type ActionType =
+  | "LOGIN_REQUEST"
+  | "LOGIN_SUCCESS"
+  | "LOGIN_FAILURE_DISMISS"
+  | "LOGIN_FAILURE"
+  | "LOGOUT";
+
+export type Action = {
+  type: ActionType;
+  username?: string | null;
+  userToken?: string | null;
+  error?: string | null;
+};

+ 64 - 0
app/src/context/auth/actions.ts

@@ -0,0 +1,64 @@
+import { Dispatch } from "react";
+import { Action } from "./actionTypes";
+import { login as apiLoginRequest } from "../../api";
+import keys from "../storageKeys";
+
+// Accion asincrona: Loguea al usuario
+export const login = (username: string, password: string) => async (
+  dispatch: Dispatch<Action>
+) => {
+  dispatch(loginRequest());
+
+  const errors = {
+    400: "Credenciales inválidas",
+  };
+
+  try {
+    const res = await apiLoginRequest(username, password);
+    if (!res.ok) {
+      const msg = (errors as any)[res.status];
+      dispatch(loginFailure(msg ?? "Request inválida"));
+      const text = await res.text();
+      console.log("Error en el login", text);
+      return;
+    }
+
+    const body = await res.json();
+
+    const userToken = body.token;
+    localStorage.setItem(keys.userToken, userToken);
+
+    dispatch(loginSuccess(username, userToken));
+  } catch (e) {
+    console.error(e);
+    dispatch(loginFailure("Error de conexión"));
+  }
+};
+
+// Accion: estado de carga durante login pendiente
+export const loginRequest = (): Action => ({
+  type: "LOGIN_REQUEST",
+});
+
+// Accion: Estado de login exitoso
+export const loginSuccess = (username: string, userToken: string): Action => ({
+  type: "LOGIN_SUCCESS",
+  username,
+  userToken,
+});
+
+// Accion: Estado de login fallido
+export const loginFailure = (code: string): Action => ({
+  type: "LOGIN_FAILURE",
+  error: code,
+});
+
+// Accion: Quitar estado de error
+export const failureDismiss = (): Action => ({
+  type: "LOGIN_FAILURE_DISMISS",
+});
+
+// Accion: logout
+export const logout = (): Action => ({
+  type: "LOGOUT",
+});

+ 58 - 0
app/src/context/auth/reducer.ts

@@ -0,0 +1,58 @@
+import { Reducer } from "react";
+import { Action } from "./actionTypes";
+import keys from "../storageKeys";
+
+export const defaultState = {
+  loggingIn: false,
+  loggedIn: false,
+  userToken: null,
+  username: null,
+};
+
+// Estado inicial de usuario
+export const getInitialState = (): State => {
+  const userToken = localStorage.getItem(keys.userToken);
+  return { ...defaultState, userToken, loggedIn: !!userToken };
+};
+
+export type State = {
+  loggingIn?: Boolean | null;
+  loggedIn?: Boolean | null;
+  userToken?: string | null;
+  username?: string | null;
+  error?: string | null;
+};
+
+const reducer: Reducer<State, Action> = (state, action) => {
+  switch (action.type) {
+    case "LOGIN_REQUEST":
+      return {
+        ...state,
+        loggingIn: true,
+        loggedIn: false,
+        username: action.username,
+      };
+    case "LOGIN_SUCCESS":
+      return {
+        loggedIn: true,
+        loggingIn: false,
+        username: action.username,
+        userToken: action.userToken,
+      };
+    case "LOGIN_FAILURE_DISMISS":
+      return {
+        ...state,
+        error: undefined,
+      };
+    case "LOGIN_FAILURE":
+      return {
+        error: action.error,
+      };
+    case "LOGOUT":
+      return {};
+    default:
+      return state;
+  }
+};
+
+export default reducer;

+ 3 - 0
app/src/context/storageKeys.ts

@@ -0,0 +1,3 @@
+export default {
+  userToken: "auth_token",
+};

+ 27 - 12
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"
@@ -5002,11 +5022,6 @@
     "loader-utils" "^2.0.0"
     "schema-utils" "^3.0.0"
 
-"file-uri-to-path@1.0.0":
-  "integrity" "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
-  "resolved" "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz"
-  "version" "1.0.0"
-
 "filesize@6.1.0":
   "integrity" "sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg=="
   "resolved" "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz"
@@ -5116,9 +5131,9 @@
     "readable-stream" "^2.3.6"
 
 "follow-redirects@^1.0.0":
-  "integrity" "sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA=="
-  "resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.2.tgz"
-  "version" "1.13.2"
+  "integrity" "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g=="
+  "resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz"
+  "version" "1.14.4"
 
 "for-in@^1.0.2":
   "integrity" "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA="

+ 0 - 6
package-lock.json

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