Преглед на файлове

Mostrar datos de la api en las tablas

wilitp преди 4 години
родител
ревизия
c13d0962ea

+ 76 - 4
app/src/api/tables.ts

@@ -1,9 +1,11 @@
 import { apiURL } from "../config";
-import { Summary, Station } from "../types";
-import { mockPayloads, mockRequest } from "./mocks";
+import { Summary, Station, DegreeDays } from "../types";
+import { Precipitations } from "../types/precipitations";
 // Quizas llamarlos table no hacia falta,
 // pero es preferible antes de que a alguien se le olvide usar un alias cuando importe estas funciones.
 
+// Wrapper para que si la request sale mal tengamos una rejection
+// en lugar de que se resuelva y tengamos que chequar el status
 const mustFetchJson = <T>(url: string, config: RequestInit) =>
   new Promise<T>(async (resolve, reject) => {
     const res = await fetch(url, config);
@@ -20,6 +22,58 @@ const mustFetchJson = <T>(url: string, config: RequestInit) =>
     }
   });
 
+export const monthlyDegrees = (
+  from: string,
+  to: string,
+  sector: string,
+  token: string
+) => {
+  const headers = new Headers();
+  headers.append("Access-Control-Allow-Origin", "*");
+  headers.append("Authorization", `Bearer ${token}`);
+
+  const config: RequestInit = {
+    method: "GET",
+    mode: "cors",
+    headers,
+  };
+
+  const qParams = new URLSearchParams();
+  qParams.append("start_datetime", `${from}T00:00`);
+  qParams.append("end_datetime", `${to}T00:00`);
+
+  return mustFetchJson<DegreeDays[]>(
+    `${apiURL}station/${sector}/monthly-degrees?${qParams.toString()}`,
+    config
+  );
+};
+
+export const monthlyPrecip = (
+  from: string,
+  to: string,
+  sector: string,
+  token: string
+) => {
+  const headers = new Headers();
+  headers.append("Access-Control-Allow-Origin", "*");
+  headers.append("Authorization", `Bearer ${token}`);
+
+  const config: RequestInit = {
+    method: "GET",
+    mode: "cors",
+    headers,
+  };
+
+  const qParams = new URLSearchParams();
+  qParams.append("start_datetime", `${from}T00:00`);
+  qParams.append("end_datetime", `${to}T00:00`);
+
+  return mustFetchJson<Precipitations[]>(
+    `${apiURL}station/${sector}/monthly-precip?${qParams.toString()}`,
+    config
+  );
+};
+
 export const generalTable = (from: string, to: string, token: string) => {
   const headers = new Headers();
   headers.append("Access-Control-Allow-Origin", "*");
@@ -43,9 +97,27 @@ export const generalTable = (from: string, to: string, token: string) => {
 export const seasonsSummariesTable = (
   from: string,
   to: string,
-  year: string
+  sector: string,
+  token: string
 ) => {
-  return mockRequest(true, mockPayloads.seasonsSummariesTable, null);
+  const headers = new Headers();
+  headers.append("Access-Control-Allow-Origin", "*");
+  headers.append("Authorization", `Bearer ${token}`);
+
+  const qParams = new URLSearchParams();
+  qParams.append("start_datetime", `${from}T00:00`);
+  qParams.append("end_datetime", `${to}T00:00`);
+
+  const config: RequestInit = {
+    method: "GET",
+    mode: "cors",
+    headers,
+  };
+
+  return mustFetchJson<Summary[]>(
+    `${apiURL}station/${sector}/summary?${qParams.toString()}`,
+    config
+  );
 };
 
 export const sectors = (token: string) => {

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

@@ -1,13 +1,50 @@
-import React, { FC } from "react";
+import React, { FC, useState, useEffect, useContext } from "react";
+import { monthlyDegrees } from "../../../api";
+import { UserStateContext } from "../../../context/auth/AuthProvider";
+import { StateContext } from "../../../context/dashboard/Provider";
+import { DegreeDays } from "../../../types";
 import * as classes from "../tables.module.css";
 
-interface DegreeDayProps {
+interface DegreesProps {
   title: String;
   periodString: String;
 }
 
-const DegreeDay: FC<DegreeDayProps> = ({ title, periodString }) => {
-  const suffix = "Precipitaciones mensuales";
+// TODO: Computar el string de temporada
+// Consigue un string para que el usuario identifique la temporada que esta viendo
+const campString = (ISOFrom: string, ISOTo: string) => {
+  const from = new Date(Date.parse(ISOFrom));
+  const to = new Date(Date.parse(ISOTo));
+
+  return `${ISOFrom.slice(0, 10)} / ${ISOTo.slice(0, 10)}`;
+};
+
+const DegreeDay: FC<DegreesProps> = ({ title, periodString }) => {
+  const [data, setData] = useState<DegreeDays[] | null>(null);
+  const { userToken } = useContext(UserStateContext);
+  const { from, to, sector } = useContext(StateContext);
+
+  useEffect(() => {
+    if (!userToken || !sector) return;
+    monthlyDegrees(from, to, sector, userToken).then(setData);
+  }, [from, to, userToken, sector]);
+
+  const suffix = "Grados días promedio mensuales";
+
+  const rows = data?.map((x) => (
+    <tr key={x.initial_date}>
+      <th className={classes.cell}>
+        {campString(x.initial_date, x.final_date)}
+      </th>
+      <td className={classes.cell}>{x.months[10] ?? "-"}</td>
+      <td className={classes.cell}>{x.months[11] ?? "-"}</td>
+      <td className={classes.cell}>{x.months[12] ?? "-"}</td>
+      <td className={classes.cell}>{x.months[1] ?? "-"}</td>
+      <td className={classes.cell}>{x.months[2] ?? "-"}</td>
+      <td className={classes.cell}>{x.months[3] ?? "-"}</td>
+    </tr>
+  ));
+
   return (
     <section>
       <h3>{`${title} - ${suffix}`}</h3>
@@ -22,32 +59,22 @@ const DegreeDay: FC<DegreeDayProps> = ({ title, periodString }) => {
             <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>
+        <tbody>{data && rows}</tbody>
       </table>
+      {!data && (
+        <div className="d-flex py-3 justify-content-center">
+          <div className="spinner-border" role="status">
+            <span className="visually-hidden">Loading...</span>
+          </div>
+        </div>
+      )}
+      {data && data.length === 0 && (
+        <div className="d-flex py-3 justify-content-center">
+          <p>Sin datos</p>
+        </div>
+      )}
     </section>
   );
 };

+ 19 - 10
app/src/components/data/GeneralPerSeason/index.tsx

@@ -1,30 +1,34 @@
-import React, { FC, useEffect, useState } from "react";
+import React, { FC, useEffect, useContext, useState } from "react";
 import { seasonsSummariesTable } from "../../../api/tables";
 import { Summary } from "../../../types/summary";
 import { TableHeader as Header, TdGroup } from "../shared";
 import * as classes from "../tables.module.css";
+import { StateContext as DashboardContext } from "../../../context/dashboard/Provider";
+import { UserStateContext } from "../../../context/auth/AuthProvider";
 
 const TemperaturePerSeason: FC = () => {
   const [data, setData] = useState<Summary[] | null>(null);
 
+  const { sector, from, to } = useContext(DashboardContext);
+  const { userToken } = useContext(UserStateContext);
+
   useEffect(() => {
-    seasonsSummariesTable("", "", "").then((res) => {
-      setData(JSON.parse(res));
-    });
-  }, []);
+    if (!sector || !userToken) return;
+    seasonsSummariesTable(from, to, sector, userToken).then(setData);
+  }, [from, to, sector, userToken]);
 
   const rows = data?.map((x) => (
-    <tr>
+    <tr key={x.initial_date}>
       <th className={classes.cell}>Temporada 2015-2016 - Vendimia 2016</th>
       <TdGroup>
-        <td>4,51%</td>
-        <td>17,86%</td>
-        <td>8,84%</td>
+        <td>{x.lt10 ?? "-"}%</td>
+        <td>{x.gt30 ?? "-"}%</td>
+        <td>{x.gt33 ?? "-"}%</td>
       </TdGroup>
       <td className={classes.cell}>{x.grados_acumulados}</td>
+      <td className={classes.cell}>{x.dias_igualar_temporada}</td>
       <td className={classes.cell}>{x.amplitud_termica}</td>
       <td className={classes.cell}>{x.precip_acumulada}</td>
-      <td className={classes.cell}>{x.dias_para_igualar_temporada}</td>
     </tr>
   ));
 
@@ -41,6 +45,11 @@ const TemperaturePerSeason: FC = () => {
           </div>
         </div>
       )}
+      {data && data.length === 0 && (
+        <div className="d-flex py-3 justify-content-center">
+          <p>Sin datos</p>
+        </div>
+      )}
     </>
   );
 };

+ 15 - 5
app/src/components/data/GeneralPerSector/index.tsx

@@ -9,19 +9,22 @@ import * as classes from "../tables.module.css";
 const GeneralPerSector: FC = () => {
   const [data, setData] = useState<Summary[] | null>(null);
   const { userToken } = useContext(UserStateContext);
-  const { from, to } = useContext(StateContext);
+  const { from, to, sector } = useContext(StateContext);
 
   useEffect(() => {
     if (!userToken) return;
-    generalTable(from, to, userToken).then((res) => {
-      setData(res);
-    });
+    generalTable(from, to, userToken).then(setData);
   }, [from, to, userToken]);
 
   // Formato de los summaries(filas)
   const rows = data?.map((x) => (
     <tr key={x.station.station_code}>
-      <th className={classes.cell}>{x.station.title}</th>
+      <th
+        className={classes.cell}
+        style={{ color: x.station.station_code === sector ? "red" : undefined }}
+      >
+        {x.station.title}
+      </th>
       <TdGroup>
         <td>{x.lt10 ?? "-"}%</td>
         <td>{x.gt30 ?? "-"}%</td>
@@ -49,6 +52,13 @@ const GeneralPerSector: FC = () => {
           </div>
         </div>
       )}
+      {data && data.length === 0 && (
+        <div className="d-flex py-3 justify-content-center">
+          <div className="spinner-border" role="status">
+            <p>Sin datos</p>
+          </div>
+        </div>
+      )}
     </>
   );
 };

+ 50 - 22
app/src/components/data/Precipitation/index.tsx

@@ -1,4 +1,8 @@
-import React, { FC } from "react";
+import React, { FC, useState, useEffect, useContext } from "react";
+import { monthlyPrecip } from "../../../api";
+import { UserStateContext } from "../../../context/auth/AuthProvider";
+import { StateContext } from "../../../context/dashboard/Provider";
+import { Precipitations } from "../../../types";
 import * as classes from "../tables.module.css";
 
 interface PrecipitationProps {
@@ -6,8 +10,39 @@ interface PrecipitationProps {
   periodString: String;
 }
 
+const campString = (ISOFrom: string, ISOTo: string) => {
+  const from = new Date(Date.parse(ISOFrom));
+  const to = new Date(Date.parse(ISOTo));
+
+  return `${ISOFrom.slice(0, 10)} / ${ISOTo.slice(0, 10)}`;
+};
+
 const Precipitation: FC<PrecipitationProps> = ({ title, periodString }) => {
-  const suffix = "Grados días promedios mensuales";
+  const [data, setData] = useState<Precipitations[] | null>(null);
+  const { userToken } = useContext(UserStateContext);
+  const { from, to, sector } = useContext(StateContext);
+
+  useEffect(() => {
+    if (!userToken || !sector) return;
+    monthlyPrecip(from, to, sector, userToken).then(setData);
+  }, [from, to, userToken, sector]);
+
+  const suffix = "Precipitaciones mensuales";
+
+  const rows = data?.map((x) => (
+    <tr key={x.initial_date}>
+      <th className={classes.cell}>
+        {campString(x.initial_date, x.final_date)}
+      </th>
+      <td className={classes.cell}>{x.months[10] ?? "-"}</td>
+      <td className={classes.cell}>{x.months[11] ?? "-"}</td>
+      <td className={classes.cell}>{x.months[12] ?? "-"}</td>
+      <td className={classes.cell}>{x.months[1] ?? "-"}</td>
+      <td className={classes.cell}>{x.months[2] ?? "-"}</td>
+      <td className={classes.cell}>{x.months[3] ?? "-"}</td>
+    </tr>
+  ));
+
   return (
     <section>
       <h3>{`${title} - ${suffix}`}</h3>
@@ -24,27 +59,20 @@ const Precipitation: FC<PrecipitationProps> = ({ title, periodString }) => {
             <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>
+        <tbody>{data && rows}</tbody>
       </table>
+      {!data && (
+        <div className="d-flex py-3 justify-content-center">
+          <div className="spinner-border" role="status">
+            <span className="visually-hidden">Loading...</span>
+          </div>
+        </div>
+      )}
+      {data && data.length === 0 && (
+        <div className="d-flex py-3 justify-content-center">
+          <p>Sin datos</p>
+        </div>
+      )}
     </section>
   );
 };

+ 29 - 30
app/src/components/pages/Home.tsx

@@ -1,12 +1,8 @@
 import React, { useContext, FC, useState } from "react";
-// import DegreeDay from "../data/DegreeDay";
-// import TemperaturePerSeason from "../data/TemperaturePerSeason";
 import GeneralPerSector from "../data/GeneralPerSector";
 import GeneralPerSeason from "../data/GeneralPerSeason";
 import DegreeDay from "../data/DegreeDay";
-import HeatMap from "../data/HeatMap";
 import Precipitation from "../data/Precipitation";
-// import Precipitation from "../data/Precipitation";
 import { UserStateContext } from "../../context/auth/AuthProvider";
 import DashboardProvider from "../../context/dashboard/Provider";
 import { Redirect } from "react-router-dom";
@@ -19,9 +15,9 @@ const Home: FC = () => {
   const [viewSeasonState, setViewSeasonState] = useState<Boolean>(false);
   const [viewDegreeState, setViewDegreeState] = useState<Boolean>(false);
   const [viewPrepState, setViewPrepState] = useState<Boolean>(false);
-  const toggleSeason = () => setViewSeasonState(!viewSeasonState)
-  const toggleDegree = () => setViewDegreeState(!viewDegreeState)
-  const togglePrep = () => setViewPrepState(!viewPrepState)
+  const toggleSeason = () => setViewSeasonState(!viewSeasonState);
+  const toggleDegree = () => setViewDegreeState(!viewDegreeState);
+  const togglePrep = () => setViewPrepState(!viewPrepState);
 
   if (!userState.loggedIn) {
     return <Redirect to="/login" />;
@@ -38,30 +34,33 @@ const Home: FC = () => {
             </div>
           </section>
 
-          <CheckboxTables changeSeason={toggleSeason} changeDegree={toggleDegree} changePrep={togglePrep}/>
-
-          { viewSeasonState ?
-          <section className="row">
-            <div className="col-xl-8">
-              <GeneralPerSeason />
-            </div>
-          </section>
-          : null }
-          { viewDegreeState ?  
-          <section className="row">
-            <div className="col-xl-8">
-              <DegreeDay title="hola" periodString="0/0/0" />
-            </div>
-          </section> 
-          : null }
-          { viewPrepState ?
-          <section className="row">
-            <div className="col-xl-8">
-              <Precipitation title="hola" periodString="0/0/0" />
-            </div>
-          </section>
-          : null }
+          <CheckboxTables
+            changeSeason={toggleSeason}
+            changeDegree={toggleDegree}
+            changePrep={togglePrep}
+          />
 
+          {viewSeasonState ? (
+            <section className="row">
+              <div className="col-xl-8">
+                <GeneralPerSeason />
+              </div>
+            </section>
+          ) : null}
+          {viewDegreeState ? (
+            <section className="row">
+              <div className="col-xl-8">
+                <DegreeDay title="hola" periodString="0/0/0" />
+              </div>
+            </section>
+          ) : null}
+          {viewPrepState ? (
+            <section className="row">
+              <div className="col-xl-8">
+                <Precipitation title="holalol" periodString="0/0/0" />
+              </div>
+            </section>
+          ) : null}
         </DashboardProvider>
       </Layout>
     </>

+ 12 - 0
app/src/types/degreedays.ts

@@ -0,0 +1,12 @@
+export interface DegreeDays {
+  station: {
+    station_code: string;
+    title: string;
+  };
+  initial_date: string;
+  final_date: string;
+  months: {
+    [index: string]: number;
+  };
+  precip_acumulada: number;
+}

+ 2 - 0
app/src/types/index.ts

@@ -1,2 +1,4 @@
 export * from "./station";
 export * from "./summary";
+export * from "./precipitations";
+export * from "./degreedays";

+ 12 - 0
app/src/types/precipitations.ts

@@ -0,0 +1,12 @@
+export interface Precipitations {
+  station: {
+    station_code: string;
+    title: string;
+  };
+  initial_date: string;
+  final_date: string;
+  months: {
+    [index: string]: number;
+  };
+  precip_acumulada: number;
+}

+ 3 - 1
app/src/types/summary.ts

@@ -15,5 +15,7 @@ export interface Summary {
   precip_acumulada: number;
 
   // Intigers
-  dias_para_igualar_temporada: number;
+  dias_igualar_temporada: number;
+  initial_date: string;
+  final_date: string;
 }