diff --git a/.env b/.env new file mode 100644 index 0000000..483b6bc --- /dev/null +++ b/.env @@ -0,0 +1 @@ +VITE_API=http://127.0.0.1:8000 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b448757..b168b7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "frontend-alerta-rimac", "version": "0.0.0", "dependencies": { + "axios": "^1.7.2", "leaflet": "^1.9.4", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -15,7 +16,8 @@ "react-leaflet-cluster": "^2.1.0", "react-router": "^6.23.1", "react-router-dom": "^6.23.1", - "socket.io-client": "^4.7.5" + "socket.io-client": "^4.7.5", + "zustand": "^4.5.2" }, "devDependencies": { "@types/react": "^18.3.3", @@ -1372,14 +1374,14 @@ "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@types/react": { "version": "18.3.3", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -1671,6 +1673,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/autoprefixer": { "version": "10.4.19", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", @@ -1725,6 +1733,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1933,6 +1952,18 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -1989,7 +2020,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/data-view-buffer": { @@ -2106,6 +2137,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -2851,6 +2891,26 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -2878,6 +2938,20 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -3902,6 +3976,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4519,6 +4614,12 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5576,6 +5677,15 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -5952,6 +6062,34 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.2.tgz", + "integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index 9973231..c47e1cc 100644 --- a/package.json +++ b/package.json @@ -10,14 +10,16 @@ "preview": "vite preview" }, "dependencies": { + "axios": "^1.7.2", "leaflet": "^1.9.4", "react": "^18.3.1", "react-dom": "^18.3.1", - "socket.io-client": "^4.7.5", "react-leaflet": "^4.2.1", "react-leaflet-cluster": "^2.1.0", "react-router": "^6.23.1", - "react-router-dom": "^6.23.1" + "react-router-dom": "^6.23.1", + "socket.io-client": "^4.7.5", + "zustand": "^4.5.2" }, "devDependencies": { "@types/react": "^18.3.3", diff --git a/src/Components/ContainerNotification.jsx b/src/Components/ContainerNotification.jsx index ed3b4d8..b29ab99 100644 --- a/src/Components/ContainerNotification.jsx +++ b/src/Components/ContainerNotification.jsx @@ -1,10 +1,12 @@ import { useEffect, useState } from "react"; import { io } from "socket.io-client"; import Notification from "./Notification"; +import dateFormater from "../utils/dateFormater"; -const ContainerNotification = () => { - const [notification, setNotification] = useState(""); - const [date, setDate] = useState(new Date()); +const ContainerNotification = ({flag, setFlag}) => { + const [title, setTitle] = useState("Alerta RIMAC"); + const [description, setDescription] = useState(""); + const [date, setDate] = useState(""); const [statusNotification, setStatusNotification] = useState(false); useEffect(() => { @@ -12,8 +14,10 @@ const ContainerNotification = () => { socket.on("notification", (data) => { setStatusNotification(true); - setNotification(data?.message?.gemini_output); - setDate(new Date(data.date)); + setFlag(!flag); + setTitle(data?.evento?.tipo); + setDescription(data?.evento?.gemini_output); + setDate(dateFormater(data?.evento?.fecha)); console.log(data); // Establecer un temporizador para ocultar la notificación después de 3 segundos @@ -34,9 +38,9 @@ const ContainerNotification = () => { statusNotification ? "translate-y-0 opacity-100" : "-translate-y-full opacity-0" - } transition-all duration-500 absolute`} + } transition-all duration-500 absolute z-50`} > - + ); }; diff --git a/src/Components/Detalles.jsx b/src/Components/Detalles.jsx index 8b4301b..5acf3da 100644 --- a/src/Components/Detalles.jsx +++ b/src/Components/Detalles.jsx @@ -1,27 +1,32 @@ -import Logo from "../assets/logoxd.png"; -import Maps from "../assets/logoxd.png"; import { NavLink } from "react-router-dom"; import "leaflet/dist/leaflet.css"; +import { useEffect } from "react"; +import useEventsStore from "../store/eventsData"; -import { MapContainer, TileLayer, Marker, Popup, Circle } from "react-leaflet"; -const Detalles = ({ evento }) => { + +const Detalles = () => { + const {events} = useEventsStore(); + useEffect(() => { + console.log(events, "eventos desde dettales"); + } + ,[events]); return ( <>
-
+

Evento

-

{evento && evento.description}

+

{events[0].tipo}

Fecha y Hora

-

{evento && evento.date}

+

{events[0].fecha}

-
+ {/*

Recomendaciones

-

{evento && evento.recomendaciones}

-
+

{"por programar"}

+
*/} {/*

Lugar

@@ -38,9 +43,11 @@ const Detalles = ({ evento }) => {
*/} - +
+

Todo va a estar bien

+
- diff --git a/src/Components/Evento.jsx b/src/Components/Evento.jsx index fd0cdc2..1083164 100644 --- a/src/Components/Evento.jsx +++ b/src/Components/Evento.jsx @@ -1,20 +1,26 @@ import React from "react"; -import Ambulancia from "../assets//IconsImages/Ambulancia.png"; -import { MapContainer, TileLayer, Marker, Popup, Circle } from "react-leaflet"; +import alerta from "../assets/alerta.png"; +import dateFormater from "../utils/dateFormater"; +import { NavLink } from "react-router-dom"; + const Evento = ({ evento }) => { return (
- {evento.tipo} + {evento.tipo}
-
-

{evento.titulo}

-

{evento.tiempo}

+
+

+ {evento.tipo.split(" ").slice(0, 2).join(" ")} +

+

{dateFormater(evento.fecha)}

-

{evento.descripcion}

- +

{evento.tipo}

+ + +
diff --git a/src/Components/Map.jsx b/src/Components/Map.jsx index cd8f8c8..f9d9cf0 100644 --- a/src/Components/Map.jsx +++ b/src/Components/Map.jsx @@ -13,6 +13,14 @@ import EventosEspeciales from "../assets/IconsImages/EventosEspeciales.png"; import MaterialesPeligrosos from "../assets/IconsImages/MaterialesPeligrosos.png"; import Sismo from "../assets/IconsImages/Sismo.png"; import { NavLink, useNavigate } from "react-router-dom"; +import useEventsStore from "../store/eventsData"; + +const customIcon = new Icon({ + // iconUrl: "https://cdn-icons-png.flaticon.com/512/447/447031.png", + iconUrl: "https://cdn-icons-png.flaticon.com/512/447/447031.png", + iconSize: [38, 38], // size of the icon +}); +/* const customAbulanciaIcon = new Icon({ iconUrl: ambulanceIcon, @@ -42,7 +50,8 @@ const customSismoIcon = new Icon({ iconUrl: Sismo, iconSize: [38, 38], // size of the icon }); - +*/ +/* let markers = [ { geocode: [-12.092918, -77.025284], @@ -62,7 +71,7 @@ let markers = [ type: customSismoIcon, id: 3, }, -]; +];*/ const createClusterCustomIcon = function (cluster) { return new divIcon({ html: `${cluster.getChildCount()}`, @@ -73,8 +82,11 @@ const createClusterCustomIcon = function (cluster) { const Map = ({ latitud, longitud, eventosMapa }) => { const position = [latitud, longitud]; + const { events } = useEventsStore(); const navigate = useNavigate(); - + useEffect(() => { + console.log(events, "eventos desde map"); + }, [events]); return (
@@ -92,16 +104,16 @@ const Map = ({ latitud, longitud, eventosMapa }) => { iconCreateFunction={createClusterCustomIcon} > {/*{ lat: marker.latitud, lng: marker.longitud } */} - {eventosMapa && - markers.map((marker) => ( + {events && + events.map((marker) => ( - Ver más + Ver más diff --git a/src/Components/MapEvents.jsx b/src/Components/MapEvents.jsx index f702fa3..20672e6 100644 --- a/src/Components/MapEvents.jsx +++ b/src/Components/MapEvents.jsx @@ -1,13 +1,22 @@ -import React from "react"; +import React, { useEffect } from "react"; import "leaflet/dist/leaflet.css"; import Map from "./Map"; import Evento from "./Evento"; +import useEventsStore from "../store/eventsData"; + + const MapEvents = ({ latitud, longitud, eventos }) => { + const {events} = useEventsStore(); + useEffect(() => { + console.log(events, "eventos desde eventDetails"); + } + ,[events]); + return (
- - {eventos && - eventos.map((evento) => )} + + {events && + events.map((evento) => )}
); }; diff --git a/src/api/peticiones.api.js b/src/api/peticiones.api.js new file mode 100644 index 0000000..a2fa0a0 --- /dev/null +++ b/src/api/peticiones.api.js @@ -0,0 +1,19 @@ +import { instanseAxios } from "./useAxios.js"; + +export const getByStatus = async () => { + try { + const { data } = await instanseAxios.get(`/eventos/get_eventos_by_state`); + return data; + } catch (error) { + console.error(error); + } +}; + +export const getById = async (id) => { + try { + const { data } = await instanseAxios.get(`/eventos/get_evento/${id}`); + return data; + } catch (error) { + console.error(error); + } +}; \ No newline at end of file diff --git a/src/api/useAxios.js b/src/api/useAxios.js new file mode 100644 index 0000000..e6ddc96 --- /dev/null +++ b/src/api/useAxios.js @@ -0,0 +1,8 @@ +import axios from "axios"; + +export const instanseAxios = axios.create({ + baseURL: "http://127.0.0.1:8000", + headers: { + "Content-Type": "application/json", + }, +}); \ No newline at end of file diff --git a/src/assets/alerta.png b/src/assets/alerta.png new file mode 100644 index 0000000..7980e1a Binary files /dev/null and b/src/assets/alerta.png differ diff --git a/src/assets/placeholder.png b/src/assets/placeholder.png new file mode 100644 index 0000000..27e56f4 Binary files /dev/null and b/src/assets/placeholder.png differ diff --git a/src/class.jsx b/src/class.jsx index 1df90d6..b818169 100644 --- a/src/class.jsx +++ b/src/class.jsx @@ -6,12 +6,14 @@ import { createBrowserRouter, useLoaderData, } from "react-router-dom"; -import Notification from "./Components/Notification"; +import { getById, getByStatus } from "./api/peticiones.api"; import { useEffect, useState } from "react"; import Detalles from "./Components/Detalles"; import MapEvents from "./Components/MapEvents"; import Loading from "./Components/Loading"; import { useNavigate } from "react-router-dom"; +import ContainerNotification from "./Components/ContainerNotification"; +import useEventsStore from "./store/eventsData"; const router = createBrowserRouter([ { @@ -30,7 +32,32 @@ const router = createBrowserRouter([ ]); export default function App() { - return ; + const [flag, setFlag] = useState(false); + const { events, addEvent } = useEventsStore(); + useEffect(() => { + console.log("flag", flag); + const fetchData = async () => { + if (events[0]!=undefined) { + const result = await getById(events[events.lenght - 1].id+1); + console.log(result); + addEvent(result); + console.log(events, "eventos"); + } else { + const result = await getById(100); + console.log(result); + addEvent(result); + console.log(events, "eventos"); + } + }; + + fetchData(); + }, [flag]); + return ( +
+ + ; +
+ ); } function Root() { diff --git a/src/main.jsx b/src/main.jsx index 6e83fd9..ffbdf84 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -4,7 +4,5 @@ import "./index.css"; import App from "./class"; ReactDOM.createRoot(document.getElementById("root")).render( - - ); diff --git a/src/store/eventsData.js b/src/store/eventsData.js new file mode 100644 index 0000000..4d2fa1c --- /dev/null +++ b/src/store/eventsData.js @@ -0,0 +1,10 @@ +import { create } from "zustand"; + +const useEventsStore = create((set) => ({ + events: [], + addEvent: (newEvent) => set((state) => ({ + events: [...state.events, newEvent] + })), + })); + + export default useEventsStore; \ No newline at end of file diff --git a/src/utils/dateFormater.js b/src/utils/dateFormater.js new file mode 100644 index 0000000..50fefc2 --- /dev/null +++ b/src/utils/dateFormater.js @@ -0,0 +1,39 @@ +export default function tiempoTranscurrido(fechaString) { + // Parsear el string de fecha en un objeto Date + const fecha = new Date(fechaString); + const ahora = new Date(); + + // Calcular la diferencia en milisegundos + const diferencia = ahora - fecha; + + // Definir los milisegundos en una unidad de tiempo + const unSegundo = 1000; + const unMinuto = unSegundo * 60; + const unaHora = unMinuto * 60; + const unDia = unaHora * 24; + const unaSemana = unDia * 7; + const unMes = unDia * 30; + const unAno = unDia * 365; + + if (diferencia < unMinuto) { + return "Hace un momento"; + } else if (diferencia < unaHora) { + const minutos = Math.floor(diferencia / unMinuto); + return `Hace ${minutos} ${minutos === 1 ? 'minuto' : 'minutos'}`; + } else if (diferencia < unDia) { + const horas = Math.floor(diferencia / unaHora); + return `Hace ${horas} ${horas === 1 ? 'hora' : 'horas'}`; + } else if (diferencia < unaSemana) { + const dias = Math.floor(diferencia / unDia); + return `Hace ${dias} ${dias === 1 ? 'día' : 'días'}`; + } else if (diferencia < unMes) { + const semanas = Math.floor(diferencia / unaSemana); + return `Hace ${semanas} ${semanas === 1 ? 'semana' : 'semanas'}`; + } else if (diferencia < unAno) { + const meses = Math.floor(diferencia / unMes); + return `Hace ${meses} ${meses === 1 ? 'mes' : 'meses'}`; + } else { + const anos = Math.floor(diferencia / unAno); + return `Hace ${anos} ${anos === 1 ? 'año' : 'años'}`; + } +} \ No newline at end of file