basic ui
This commit is contained in:
		
							
								
								
									
										25
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										25
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -10,10 +10,12 @@ | ||||
|       "dependencies": { | ||||
|         "@solidjs/router": "^0.14.9", | ||||
|         "bulma": "^1.0.2", | ||||
|         "leaflet": "^1.9.4", | ||||
|         "solid-icons": "^1.1.0", | ||||
|         "solid-js": "^1.9.1" | ||||
|       }, | ||||
|       "devDependencies": { | ||||
|         "@types/leaflet": "^1.9.12", | ||||
|         "sass": "^1.80.1", | ||||
|         "typescript": "^5.5.3", | ||||
|         "vite": "^5.4.8", | ||||
| @@ -1826,6 +1828,23 @@ | ||||
|       "dev": true, | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/@types/geojson": { | ||||
|       "version": "7946.0.14", | ||||
|       "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", | ||||
|       "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", | ||||
|       "dev": true, | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/@types/leaflet": { | ||||
|       "version": "1.9.12", | ||||
|       "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.12.tgz", | ||||
|       "integrity": "sha512-BK7XS+NyRI291HIo0HCfE18Lp8oA30H1gpi1tf0mF3TgiCEzanQjOqNZ4x126SXzzi2oNSZhZ5axJp1k0iM6jg==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@types/geojson": "*" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/resolve": { | ||||
|       "version": "1.20.2", | ||||
|       "dev": true, | ||||
| @@ -3250,6 +3269,12 @@ | ||||
|         "node": ">=0.10.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/leaflet": { | ||||
|       "version": "1.9.4", | ||||
|       "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", | ||||
|       "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", | ||||
|       "license": "BSD-2-Clause" | ||||
|     }, | ||||
|     "node_modules/leven": { | ||||
|       "version": "3.1.0", | ||||
|       "dev": true, | ||||
|   | ||||
| @@ -11,10 +11,12 @@ | ||||
|   "dependencies": { | ||||
|     "@solidjs/router": "^0.14.9", | ||||
|     "bulma": "^1.0.2", | ||||
|     "leaflet": "^1.9.4", | ||||
|     "solid-icons": "^1.1.0", | ||||
|     "solid-js": "^1.9.1" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@types/leaflet": "^1.9.12", | ||||
|     "sass": "^1.80.1", | ||||
|     "typescript": "^5.5.3", | ||||
|     "vite": "^5.4.8", | ||||
|   | ||||
							
								
								
									
										6
									
								
								src/constants/Photos.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/constants/Photos.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| /** Lista de fotos (Licencia CC) */ | ||||
| const PHOTOS: string[] = [ | ||||
|   "https://images.pexels.com/photos/2532003/pexels-photo-2532003.jpeg?cs=srgb&dl=pexels-jamie-patterson-1318696-2532003.jpg&fm=jpg" | ||||
| ]; | ||||
|  | ||||
| export default PHOTOS; | ||||
| @@ -1,6 +0,0 @@ | ||||
| /** Lista de videos (CC) */ | ||||
| const VIDEOS: string[] = [ | ||||
|   "https://www.pexels.com/es-es/download/video/13831212/?fps=60.0&h=720&w=1280" | ||||
| ]; | ||||
|  | ||||
| export default VIDEOS; | ||||
							
								
								
									
										0
									
								
								src/helpers/Api.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/helpers/Api.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -1,15 +1,15 @@ | ||||
| import VIDEOS from "../constants/Videos"; | ||||
| import PHOTOS from "../constants/Photos"; | ||||
| 
 | ||||
| /** | ||||
|  * Clase helper para gestionar los vídeos de fondo | ||||
|  * encontrados en views/Home | ||||
|  */ | ||||
| export default class Video { | ||||
| export default class Photo { | ||||
|   /** | ||||
|    * Elige un vídeo aleatoriamente de la lista | ||||
|    * @returns Vídeo aleatorio | ||||
|    */ | ||||
|   static random(): string { | ||||
|     return VIDEOS[Math.floor(Math.random()*VIDEOS.length)]; | ||||
|     return PHOTOS[Math.floor(Math.random()*PHOTOS.length)]; | ||||
|   } | ||||
| } | ||||
| @@ -1,6 +1,7 @@ | ||||
| /* @refresh reload */ | ||||
| import { render } from 'solid-js/web'; | ||||
| import { Router } from "@solidjs/router"; | ||||
| import 'leaflet/dist/leaflet.css'; | ||||
| import './styles/bulma.scss'; | ||||
| import routes from './routes'; | ||||
|  | ||||
|   | ||||
							
								
								
									
										27
									
								
								src/interfaces/Embalse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/interfaces/Embalse.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| export default interface Embalse { | ||||
|   // EMBALSES | ||||
|   ID: number; | ||||
|   AMBITO_NOMBRE: string; | ||||
|   EMBALSE_NOMBRE: string; | ||||
|   AGUA_TOTAL: number; | ||||
|   ELECTRICO_FLAG: boolean; | ||||
|   // LISTADO_EMBALSES | ||||
|   CODIGO: number; | ||||
|   NOMBRE: string; | ||||
|   EMBALSE: string; | ||||
|   X: string; | ||||
|   Y: string; | ||||
|   DEMARC: string; | ||||
|   CAUCE: string; | ||||
|   GOOGLE: string; | ||||
|   OPENSTREETMAP: string; | ||||
|   WIKIDATA: string; | ||||
|   PROVINCIA: string; | ||||
|   CCAA: string; | ||||
|   TIPO: string; | ||||
|   TITULAR: string; | ||||
|   USO: string; | ||||
|   COTA_CORON: string; | ||||
|   ALT_CIMIEN: string; | ||||
|   INFORME: string; | ||||
| } | ||||
| @@ -1,10 +1,15 @@ | ||||
| import { RouteDefinition } from "@solidjs/router"; | ||||
| import Home from "./views/Home"; | ||||
| import Finder from "./views/Finder"; | ||||
|  | ||||
| const routes: RouteDefinition[] = [ | ||||
|   { | ||||
|     path: '/', | ||||
|     component: Home | ||||
|   }, | ||||
|   { | ||||
|     path: '/finder', | ||||
|     component: Finder | ||||
|   } | ||||
| ]; | ||||
|  | ||||
|   | ||||
							
								
								
									
										155
									
								
								src/views/Finder.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								src/views/Finder.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,155 @@ | ||||
| import { createSignal, onCleanup, onMount } from "solid-js"; | ||||
| import L from 'leaflet'; | ||||
| import { FaSolidLocationPin } from "solid-icons/fa"; | ||||
| import Navbar from "../partials/Navbar"; | ||||
|  | ||||
| function Finder() { | ||||
|   const [pos, setPos] = createSignal<[number, number]>([0, 0]); | ||||
|   const [geo, setGeo] = createSignal(true); | ||||
|  | ||||
|   let markers: L.Marker[] = []; | ||||
|  | ||||
|   let first = true; | ||||
|   let watchId: number = -1; | ||||
|   let map: L.Map; | ||||
|  | ||||
|   let me = L.marker([0, 0]); | ||||
|  | ||||
|   /** | ||||
|    * Montar Leafmap | ||||
|    */ | ||||
|   onMount(() => { | ||||
|     map = L.map('map'); | ||||
|     L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { | ||||
|       maxZoom: 19, | ||||
|       attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>' | ||||
|     }).addTo(map); | ||||
|  | ||||
|     if (geo()) { | ||||
|       listenGeolocation(); | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   /** | ||||
|    * Limpiar watch de geolocalización si | ||||
|    * está activo | ||||
|    */ | ||||
|   onCleanup(() => { | ||||
|     if (watchId !== -1) { | ||||
|       navigator.geolocation.clearWatch(watchId); | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   /** | ||||
|    * Activar / Desactivar uso de Geolocalización | ||||
|    */ | ||||
|   const toggleGeo = () => { | ||||
|     if (geo()) { | ||||
|       navigator.geolocation.clearWatch(watchId); | ||||
|       watchId = -1; | ||||
|     } else { | ||||
|       listenGeolocation(); | ||||
|     } | ||||
|  | ||||
|     setGeo(!geo()); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Activar listener de geolocalización en tiempo real | ||||
|    */ | ||||
|   const listenGeolocation = () => { | ||||
|     watchId = navigator.geolocation.watchPosition( | ||||
|       (pos) => { | ||||
|         setPos([pos.coords.latitude, pos.coords.longitude]); | ||||
|         handleLocation(); | ||||
|       }, | ||||
|       (err) => { | ||||
|         alert(err.message); | ||||
|       } | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Escribe los puntos relevantes | ||||
|    * (Mi ubicación y los embalses cercanos) | ||||
|    */ | ||||
|   const handleLocation = () => { | ||||
|     me.setLatLng(pos()); | ||||
|     if (first) { | ||||
|       map.setView(pos(), 9); | ||||
|       me.addTo(map); | ||||
|       first = false; | ||||
|     } | ||||
|  | ||||
|     // Cleanup | ||||
|     if (markers.length > 0) { | ||||
|       markers.forEach((m) => { | ||||
|         map.removeLayer(m); | ||||
|       }) | ||||
|  | ||||
|       markers = []; | ||||
|     } | ||||
|  | ||||
|     // TODO: Hacer petición y sacar markers | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Escribe la latitud de la tupla manualmente | ||||
|    * @param val Latitud dada por el input | ||||
|    */ | ||||
|   const setLatitudeManual = (val: string) => { | ||||
|     const lat = parseFloat(val); | ||||
|  | ||||
|     setPos([lat, pos()[1]]); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Escribe la longitud de la tupla manualmente | ||||
|    * @param val longitud dada por el input | ||||
|    */ | ||||
|   const setLongitudeManual = (val: string) => { | ||||
|     const lon = parseFloat(val); | ||||
|  | ||||
|     setPos([pos()[0], lon]); | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       <Navbar /> | ||||
|       <section class="section"> | ||||
|         <div class="container"> | ||||
|           <div class="columns is-centered is-vcentered is-multiline"> | ||||
|             <div class="column is-narrow"> | ||||
|               <div class="field has-addons has-addons-centered"> | ||||
|                 <div class="control"> | ||||
|                   <input class="input" on:change={(e) => setLatitudeManual(e.target.value)} type="text" disabled={geo()} placeholder="Latitud" value={pos()[0]} /> | ||||
|                 </div> | ||||
|                 <div class="control"> | ||||
|                   <input class="input" on:change={(e) => setLongitudeManual(e.target.value)} type="text" disabled={geo()} placeholder="Longitud" value={pos()[1]} /> | ||||
|                 </div> | ||||
|                 <div class="control"> | ||||
|                   <button disabled={geo()} on:click={() => handleLocation()} class="button is-info"> | ||||
|                     Aplicar | ||||
|                   </button> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|             <div class="column is-narrow"> | ||||
|               <button on:click={() => toggleGeo()} class="button" classList={{ 'is-success': geo(), 'is-danger': !geo() }}> | ||||
|                 <span class="icon"> | ||||
|                   <FaSolidLocationPin /> | ||||
|                 </span> | ||||
|                 <span>Geolocalización</span> | ||||
|               </button> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div id="map" style={{ | ||||
|             height: '70vh' | ||||
|           }}></div> | ||||
|         </div> | ||||
|       </section> | ||||
|     </> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| export default Finder; | ||||
| @@ -1,11 +1,19 @@ | ||||
| import { FaSolidGlassWaterDroplet } from "solid-icons/fa" | ||||
| import Navbar from "../partials/Navbar" | ||||
| import { createSignal } from "solid-js" | ||||
| import Photo from "../helpers/Photo" | ||||
| import { A } from "@solidjs/router"; | ||||
|  | ||||
| function Home() { | ||||
|   const [photo, setPhoto] = createSignal(Photo.random()); | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       <Navbar /> | ||||
|       <section class="hero is-fullheight-with-navbar"> | ||||
|       <section class="hero is-fullheight" style={{ | ||||
|         "background-image": `url('${photo()}')`, | ||||
|         "background-position": 'center center', | ||||
|         "background-size": 'cover', | ||||
|       }}> | ||||
|         <div class="hero-body"> | ||||
|           <div class="container has-text-centered"> | ||||
|             <div class="columns is-centered"> | ||||
| @@ -14,6 +22,7 @@ function Home() { | ||||
|                   <div class="columns is-centered is-vcentered is-mobile"> | ||||
|                     <div class="column is-4"> | ||||
|                       <figure class="image is-128x128"> | ||||
|                         {/* TODO: Agregar icono */} | ||||
|                         <img src="https://bulma.io/assets/images/placeholders/128x128.png" /> | ||||
|                       </figure> | ||||
|                     </div> | ||||
| @@ -26,6 +35,7 @@ function Home() { | ||||
|                           <span>Malackathon</span> | ||||
|                         </span> | ||||
|                       </p> | ||||
|                       <A class="button is-primary" href="/finder">Buscar</A> | ||||
|                     </div> | ||||
|                   </div> | ||||
|                 </div> | ||||
| @@ -35,7 +45,7 @@ function Home() { | ||||
|         </div> | ||||
|       </section> | ||||
|       <section class="section"> | ||||
|         <video src="https://www.pexels.com/es-es/download/video/13831212/?fps=60.0&h=720&w=1280"></video> | ||||
|         <p>TODO: Agregar tarjetitas guapas</p> | ||||
|       </section> | ||||
|     </> | ||||
|   ) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user