This graph shows how many times the word ______ has been mentioned throughout the history of the program.
vamos a seguir desarrollando nuevas features y nuevas cositas.
Y hoy vamos a estar trabajando con base de datos y vamos a tener que hacer el backend,
porque claro, es que necesitamos hacer los pronósticos y que realmente esto ya la gente pueda empezar a votar,
sus ganadores o ganadoras de cada uno de los combates.
Entonces, vamos a revisar muy rápidamente para comentaros algunas novedades que tiene el proyecto,
para que los tengáis aquí un poquito por encima.
Por ejemplo, una cosa que es nueva, las galerías.
Ya hemos puesto las galerías, por ejemplo, Z-Link.
Es que sé que las chicas sí que las tienen porque son las primeras imágenes que teníamos del primer combate.
Y aquí tenéis la galería de imágenes de Z-Link.
Podéis utilizar el teclado para moveros a izquierda a derecha.
Izquierda a derecha, además es como infinito.
Luego tenéis también Denny Zaxter, creo que también lo tiene.
Y ya tenemos galería.
Iremos añadiendo las galerías de todos.
Todos irán añadiendo las galerías.
Todos los boxeadores tendrán su galería.
Pero poco a poco lo iremos haciendo, ¿vale?
¿Qué más tenemos?
Bueno, aparte de que algunos lo que hemos hecho es ya poner el entrenamiento.
Todavía nos falta algún entrenamiento.
Si hay alguno que falta, pues que sepas que lo puedes poner.
Y también hemos hecho que los clips, cuando le das un clic,
ahora no te lleva a la página, sino que lo que hace es que te abre una modal.
Lo cual yo creo que es mucho mejor.
Porque así, pues de esta forma podéis quedaros en la página.
En la página no vamos a decir que se vayan nuestros usuarios.
Se quedan en la misma página.
Y aquí ya directamente le puedes dar al esc.
Bueno, se supone que le podías dar al esc.
Ah, sí, sí.
Le puedes dar al esc.
Digo, ¿por qué no ha funcionado?
Le puedes dar al esc para cerrarlo.
O le puedes dar a la x.
O creo que le puedes dar al fondo también.
Sí, le puedes dar al fondo.
Bueno, pues ahí lo tienes.
Tenemos ahora una modal para ver los clips.
Una cosa interesante que os quiero enseñar del tema de los clips y de la otra modal
es que hemos utilizado la etiqueta Dialog,
que es una etiqueta que no es muy conocida por todo el mundo,
que está muy chula, que está muy chula y que tiene un soporte buenísimo
y que te facilita mucho la vida para crear modales.
Y que además ya está soportada, no voy a decir por todos los navegadores,
pero yo creo que es el 95, o sí, mira, 95, lo acabo de ver.
95%.
95,82%.
Es una etiqueta HTML que está súper bien y está pensada justamente para hacer modales.
Y funciona tanto móvil como un desktop, no tenéis ningún problema, ¿vale?
¿Y cómo funciona?
Pues fijaos, utilizas el Dialog y lo bueno que tiene es que puedes cambiarle el fondo.
¿Ves este backdrop?
El backdrop es este color que se ve aquí.
Esto sería el backdrop.
Y está súper bien porque ya lo puedes personalizar fácilmente.
Además, Tailwind tiene como una pseudo clase para que puedas personalizarlo con el color que quieras.
Ahora lo enseñaré.
Por ejemplo, si nos vamos aquí, empiezo en Randev.
Voy a levantarlo en local para que lo veamos en local.
Localhost 4, 3, 2, 1.
Vamos a poner por aquí y vamos a Shelao.
Y veis aquí este fondo.
Pues este fondo, si aquí le ponemos que en lugar de black sea así como algo así, green, no sé qué, no sé cuánto.
¿Veis?
Que ya lo ha cambiado.
Y está súper bien porque ya no tenéis que preocuparos tanto de hacer ese efecto súper difícil de poner un deep, posicionarlo y todo esto.
Que seguro que alguna vez has hecho una modal y has tenido que poner un deep, que tenga un fix y que no sé qué.
Nah, pues no necesitas esto.
No necesitas esto.
Ya con este backdrop lo puedes hacer.
No sé, no sé, ahora tengo dudas.
Pero yo creo que sí que también debería funcionar el backdrop blur en el backdrop.
A ver.
Sí, me he pasado, ¿vale?
Me he pasado un poco.
Porque es verdad que la transparencia no es tan bestia.
Pero veis que también se puede hacer con backdrop, también le puedes poner que tenga blur.
No está mal, o sea, no está mal.
Le puedes hacer lo que te dé la gana, la verdad es que está bastante bien.
¿Veis?
Que puedes hacerlo con blur.
Solo digo que lo puedes hacer, no que lo hagáis.
Pero bueno, para que lo sepáis.
Ah, no me acuerdo que tenía antes el black.
Black 70, vale.
Pues eso.
Y además, una cosa interesante que tiene el dialog también, es que lo que puedes hacer, porque normalmente las modales es algo difícil de hacer, ¿no?
Pues en el dialog lo que puedes hacer es, uno, puedes ya tener un estado y decir que esté abierta.
¿Ves? Si tú pones el atributo open, ya nada más entrar debería estar abierta.
Y está abierta, lo que pasa es que no se ve porque no tenemos los estilos y tal, pero debería aparecer, ¿vale?
Entonces, ya puedes como forzarle el estado de abierto.
Y si no, lo que puedes hacer es leer ese estado porque lo puedes hacer...
A ver, aquí no lo utilizo.
Ah, pues aquí no lo utilizo.
A ver, ¿dónde lo utilizo?
Aquí no.
Aquí.
Aquí lo utilizo.
¿Ves?
Lo puedes utilizar en JavaScript para saber si la modal está abierta o no está abierta.
Y ya, pues ahí tienes el estado interno del propio elemento.
Y otra cosa interesante es a la hora de abrirlo o cerrarlo.
Y es que para cerrarlo, pues llamas al punto close y para abrirlo tienes que llamar al punto showmodal.
Y ya lo tendrías.
Y además que puedes hacer animaciones y tal.
Bueno, solo lo quería explicar porque me parece un añadido muy interesante que además seguro que os podéis llevar o lo podéis llevar a otros sitios.
Seguro que si habéis hecho alguna vez una modal, esto va a ahorrar un montón de trabajo y que funciona súper, súper bien, ¿vale?
Eso por un lado.
Luego, otra cosa interesante.
Había un problema de estáticos, de imágenes que me comentaron mucha gente, de que las imágenes no se estaban cacheando en Vercel.
Bueno, lo que he hecho es crear un Vercel.json y lo que tenemos aquí es que le digo, oye, todas las imágenes que terminen con aviv, webp, svg, png, jpeg,
le estamos poniendo que tenga un header y el cache control, lo que he dicho es que sea público y que revalide la imagen después de 120 segundos.
Esto lo que hace es que siempre va a estar mostrando como la versión vieja y por detrás va a estar refrescándolo.
Pero esto está bien porque le va a dar la mejor experiencia al usuario y total, como tampoco cambiamos tanto las imágenes, en realidad esto lo podríamos hacer mucho más.
Pero por si acaso alguna vez cambiamos una imagen, no va a ser un problema de que le cargue una versión antigua y la próxima vez que entre ya verá la nueva.
Entonces se actualiza en segundo plano y le enseña siempre a la antigua que la tendrá cacheada y ya está.
Bueno, que han puesto un Service Worker también, han puesto un Service Worker que no sé si funciona del todo, porque he visto algunos problemillas que tenía y tal,
pero mira, ves, aquí le está bloqueado, pero entiendo que es porque es localhost. A ver por aquí, si aquí sí que funciona.
Pero deberíais notar que funciona un poquito mejor justamente gracias al Service Worker.
Lo veis, lo tenemos aquí y veis aquí que está todo cacheado desde el Service Worker.
Los Service Workers hay que decir que los carga el diablo, también os digo.
¿Por qué? Porque a veces el Service Worker, el problema que tiene es que como te lo cachea tanto, que la puedes liar bastante parda.
Porque alguna vez ha pasado que cachea todos los recursos y ya no se ven los cambios de la página, porque se cachea todo.
Así que quiero pensar, quiero pensar que no ha habido ningún problema de esto.
Así que bueno, ya veis que las peticiones de imágenes y tal, pues saldrán de los Service Workers.
Lo primero que vamos a hacer es que en los pronósticos, que es lo que queremos pronósticos, ya habíamos hecho el inicio de sesión y eso sí que funcionaba bien.
El primer problema que teníamos es que fijaos que teníamos por aquí un montón de... que todos se veían la misma imagen en todos.
Todos tenían la misma imagen. Lo primero que vamos a arreglar es que no se tengan todos la misma imagen.
Para eso se supone que me he copiado los archivos, voy a ver, porque he estado sacando todas las imágenes.
Aquí las tengo, ¿vale? Aquí las tengo, aquí las tengo, aquí las tengo. Voy a sacar las imágenes.
Voy a hacer una cosa. Aquí en Boxers tenemos las imágenes de las votaciones, que pone Vote 3.
¿Ves? Tenemos aquí a Lana, Zeling, el Mariana y Plex.
Bueno, voy a sacarlos de aquí y voy a ponerlo en barra vote o barra votes, no sé cómo...
Barra votes. Voy a ponerle votes, no sé, me llama... creo que mejor...
Vamos a ponerlo en singular, ¿vale? Voy a mover estos archivos.
Estos archivos... Bueno, los voy a borrar, porque total los voy a copiar otra vez.
Así que estos los quitamos y así los meteremos a una carpeta para no tenerlos ahí todos sueltos.
Vamos a poner vote. Entonces, ya tengo todas las imágenes menos, menos los del rey de la pista, que esto va a ser complicado.
Entonces, por ahora al rey de la pista lo ocultaremos y luego le daremos cañita al rey de la pista, ¿vale?
Entonces, ya hemos copiado las imágenes y esto lo malo es que seguramente va a romper el diseño, ¿no?
Si vamos aquí, veremos... A ver, guardo los cambios, ¿vale? Las imágenes están rotas.
Aquí está todo roto, pero es porque hemos cambiado la ruta.
Entonces, donde habíamos puesto el vote 6, aquí vamos a poner vote barra y aquí en la imagen también.
Que ahora haremos que sea dinámico esto, ¿vale? Entonces ya hemos arreglado esto.
Ahora lo que tendríamos que hacer es que todos tengan la imagen dinámica, añadir las fotos correctas de los luchadores.
Lo que voy a hacer es, como tenemos aquí toda esta parte que es como manual, voy a crearme aquí un create image root, ¿vale?
Donde le pasemos el combat number, que va a ser el number, y el team slack, ¿vale?
Que va a ser como el nombre del equipo. Si es un boxeador, solo sea su nombre.
Pero es que hay boxeadores, o sea, hay combates que son dos boxeadores, dos contra dos.
Entonces le voy a poner team slack. O le podemos llamar slack directamente. Slack, ¿vale?
Slack es la parte... Ah, mira, qué joder, qué inteligente.
Efectivamente, esto es lo que quiero hacer. Imagen barra boxers, vote, vote, el número del combate, barra el guión slack.
Slack es porque en los combates, ¿veis que tendríamos aquí equipos?
Este sería el slack, tal y como lo tenemos en la imagen, ¿vale? Vote, ¿veis?
Aquí tengo voto 3, Alana, Amablitz. Pues sería este, Alana, Amablitz.
Entonces, cada equipo, si no tiene el campo de equipo, como por ejemplo este, que no tiene el campo de equipo,
el slack será viruz o shelao. Y entonces tendríamos aquí viruz y shelao.
Y tenemos el caso especial del rey de la pista, que ya veremos cómo lo hacemos.
Pero por ahora tenemos aquí un método para crear la URL, que nos va a facilitar bastante la vida, ¿vale?
Y ahora, aquí en combates... Aquí, ¿vale?
Aquí en combates que estamos mapeando todo esto, tenemos que sacar la información de cada imagen, ¿no?
Voy a hacer aquí image left o image 1, image 2.
Y esto lo que vamos a hacer de cada uno es crear...
Vale, podría ser así. Esta podría ser la primera idea para básicamente hacerlo rápido, ¿no?
Voy a hacerlo. Create image root con index 1, ¿vale?
En lugar de index 1, aquí tenemos que pasarle el combat data.number, el número de combate.
¿Vale? Esto aquí, esto acá.
Y aquí ya se supone que podríamos tener image 1, y aquí tu, tu, tu, image 2.
Image 2. ¿Vale?
Por ahora debería volver a aparecer solo el plexis Mariana.
Vale. Ah, claro. Esto no está porque el número de combate no existe para esas imágenes.
Entonces, vamos a arreglar también esto y ya está.
Vamos a sacar del combat data, vamos a sacar el team.
Voy a hacer combat data.
Y vamos a sacar de aquí.
Vamos a sacar el teams y el boxers, ¿vale?
Y lo que vamos a hacer es que básicamente si...
¿Cómo podemos hacer esto?
Ah, como siempre es uno contra...
O sea, quiero decir, como siempre hay dos equipos,
a veces el equipo está...
Es un boxeador o a veces son dos boxeadores.
Entonces, lo que vamos a hacer aquí es sacar el primero y el segundo, ¿vale?
Entonces, vamos a tener aquí slack1, slack2,
y lo vamos a sacar de teams o de boxers, ¿vale?
Esto es una desestructuración.
Lo que estoy haciendo aquí es que, por ejemplo, si teams es un array,
esta que es la primera posición la voy a meter aquí.
Y la segunda posición, que es esta, la voy a meter aquí, ¿vale?
Es una forma más fácil y aquí lo que estoy haciendo es mirar.
Si teams no es null o undefined, entonces utilizo teams.
Pero si no existe teams, lo que voy a hacer es utilizar boxers.
Es una forma como más sencilla de en lugar de tener que utilizar if y ya está.
Y ahora con esto tendríamos el slack1.
No sé llamarle 1, 2 o izquierda-derecha.
Le voy a dejar 1, 2, pero a lo mejor tendría más sentido izquierda-derecha.
Y ya tenemos todas las imágenes.
Bueno, el rey de la pista aquí nos ha fallado.
Nos ha fallado.
Vamos a hacer una cosa.
Vamos a detectar que si es el rey de la pista, ¿vale?
El rey de la pista, esto lo importamos.
Si es el king of the hill, por ahora no vamos a mostrar nada.
Ya lo arreglaremos.
Pero con esto ya tendríamos todas las imágenes de todos los luchadores, ¿vale?
El del rey de la pista, ¿cómo lo harás?
Buena pregunta.
Tengo dos opciones, ¿vale?
Tengo dos opciones.
Uno, tengo la opción de sacar todas estas imágenes
y que la gente tenga que votar poniéndose encima de cada una de las imágenes.
O otra opción podría hacer que sea con la imagen pequeña.
Yo creo que seguramente lo voy a hacer con la imagen pequeña, ¿eh?
¿Qué es Slack?
Slack básicamente es la parte de la URL.
Si yo qué sé, si vais al periódico.com,
mira, esta sería el Slack, ¿vale?
Son URLs que se generan con nombres para hacerla más semántica, la URL.
¿Cuál es la parte más dinámica de la web de la velada?
Pues va a ser el pronóstico, seguramente.
Se tapa la cara de una boxeadora.
Vale, vamos a verlo.
Pronósticos.
Vale, ta, ta, ta.
La de CELIN, ¿no?
Sí.
A ver, esto lo que podemos hacer es que sea más pequeño, ¿eh?
Porque yo creo que esto a lo mejor es demasiado grande
y seguramente vamos a querer que sea un pelín más pequeño
para que quede en medio, pero tampoco que quede tan, tan en mitad.
Bueno, y todavía nos faltará el modo móvil, ¿eh?
Que eso a lo mejor os lo voy a dejar a vosotros para que lo hagáis.
Vale, esta imagen es esta de aquí y aquí lo podemos hacer más pequeño
porque tampoco hace falta que quede tan...
Ay, me está dando...
Estoy viendo que también tiene fondo este y la madre que lo parió.
Tenía que haberlo cambiado.
Vale, esto sería en el del medio, ¿vale?
En el del medio aquí tenía 80.
Pues vamos a poner que sea menos, como por ejemplo...
Tu, tu, tu, tu, tu, tu.
64.
No sé si es 64 o algo así.
Vale, pues algo así, ¿vale?
Tiene sentido que sea más pequeño, que a lo mejor es demasiado grande.
O podríamos hacer que algunos sean más pequeños que otros.
No sé, porque el de Plex Mariana da penita que sea...
O a lo mejor podríamos hacerlo con la altura, ¿sabes?
A lo mejor limitarlo por la altura en lugar de...
A ver, ¿por qué no salen los números?
¿Sabes? Porque no me sale H24, por ejemplo.
O H36, auto.
A ver.
Tu, tu, tu.
Claro, lo que pasa es que con la altura todavía quedan más...
Todavía quedan más...
Como es demasiado alto, ¿sabes?
Como es demasiado alto este, queda un poco...
O sea, quedan bien para los demás, pero este queda como muy chiquitito.
Queda como muy chiquitito.
Bueno, por ahora voy a dejar así, que al menos no se queda por detrás de ninguno y ya está.
Luego ya lo iremos ajustando, ¿vale?
Lo iremos ajustando, pues si no vamos a estar ahí todo el día y no va a dar tiempo para la base de datos.
Hay un detalle que sí que quiero arreglar, que es el tema de que aquí estoy generando las imágenes, pero también me gustaría generar el alt.
No sé si el alt o al menos tener el nombre del boxeador.
Así que vamos a hacer esto así, ¿vale?
Aquí vamos a tener el source para que tengamos aquí, porque lo tengo aquí vacío, ves que pone fotografía de y no te dice quién es.
Entonces, aquí lo que necesitamos es buscar de quién es el...
Al menos el nombre.
A ver, tampoco hace falta hacer nada raro, pero yo qué sé.
Voy a hacerlo sencillo y vamos a hacer que el team...
Slack 1, replace...
Vamos a quitarle los guiones y le vamos a poner un espacio.
Yo creo que con esto ya será suficiente para que al menos lea bien...
Lea bien quién es el usuario...
Quién es el boxeador sin necesidad de buscarlo, que va a ser un poco rollo, ¿vale?
Y ahora aquí, en esta imagen poníamos source y aquí vamos a poner image1.alt, ¿vale?
Y así al menos fotografía de...
Y esto mismo nos lo llevamos aquí abajo, pero con el image2, ¿vale?
Y ya está.
Y al menos ahora ya deberíamos tener el alt y un poquito mejor la accesibilidad.
Nos saltamos el rey de la pista por ahora.
Ahora hay un detalle, un detalle tonto, pero que tendríamos que tener en cuenta y es que cuando entramos en los pronósticos, el usuario no puede cerrar sesión ni nada, ¿vale?
Dejaste dos alts.
Vale, gracias por avisar.
Alta, gracias.
Entonces, vamos a arreglar esto un momentito para que pueda cerrar sesión sin ningún tipo de problema.
Vamos a ponerle aquí, haz tu pronóstico.
Vamos a copiar este typography.
Aquí debajo vamos a poner elige o vota tus ganadores para cada combate haciendo clic encima de cada uno.
Lo que sea.
Luego ya mejoraremos si eso los...
Esto, pen, white.
Y creo que este teníamos el body y teníamos el medium, ¿no?
Esto vamos a hacer que la separación sea un poquito más pequeña.
Vamos a dividir aquí.
Y ahora, este action que habíamos puesto aquí para cerrar sesión, vamos a añadir un nuevo componente donde vamos a mostrar la información del usuario con un botón de cerrar sesión.
Para que pueda cerrar sesión, ¿vale?
En algún sitio.
Porque es que si no el usuario se queda ahí con la sesión iniciada y no va a poder hacer absolutamente nada.
Vale, vamos a poner rounded, full source, alt.
Aquí vamos a poner la avatar.
Avatar.
Hostia, he puesto dos veces.
Del usuario.
Vale, cerramos esto por aquí.
Deep class.
Vamos a poner aparte aquí un botón que sea el de cerrar sesión, ¿vale?
Bueno, vamos a poner aquí el user.name y aquí vamos a ponerle el botón este de logout, cerrar sesión.
Le tenemos que pasar user.name.
Le tenemos que pasar el usuario porque fijaos que el usuario aquí no lo tenemos, se lo podemos pasar por props.
Podemos ir a los pronósticos aquí y aquí le podemos pasar el user que lo tenemos justamente aquí.
De la sesión pasamos el user.
Así que ahora aquí vamos a crearle y decirle que tenemos en las props el usuario que es del tipo user y sacamos el user de astro.props, ¿vale?
¿Qué se queja esto?
Ajá, dice que el import tiene que ir arriba.
Y con esto, a ver, vamos a tener que ajustar alguna cosa.
Vale, esto debería ser user.image.
Hostia, ¿por qué no me pilla?
Me dice que es Eni, cabrón.
Me dice que es Eni.
Esto yo creo que es la extensión de astro porque esto debería pillarlo automáticamente.
A ver ahora.
A ver, ahora lo estoy forzando, pero no debería ser necesario.
¿Puede ser que es que tenga que exportarlo?
Nunca me acuerdo si es que hay que exportarlo.
¿Puede ser?
No.
Vale, pues no tengo ni idea porque esto lo debería hacer automáticamente.
No sé por qué no lo hace.
Ahora igual me lo decís y que a lo mejor se me ha escapado algo.
Pero yo juraría que eso debería hacerlo automáticamente.
Vale, vamos a poner esto en lugar que sea así.
Vamos a poner un yellow.
Yellow, no sé, 300 o una cosa así.
Vamos a poner esto que tenga text xl font bold para que quede un poquito de cosita.
Y aquí el tema es que tendríamos que hacer que la separación que tenga sea un poquito menor.
Es que no sé por qué esto...
A ver, flex, item center, para centrarlo.
Y flex call...
No.
Justify center.
Justify center.
Vale, pero todavía como que...
Igual esto es demasiado grande.
Font light y text sm.
Y bueno, lo que no sé...
Yo creo que es visual.
No, no, es visual un coño.
Aquí hay alguna cosa que no está centrado.
Creo que no está centrado.
Bueno, da igual.
No voy a perder tiempo centrándolo, ¿vale?
Solo quería tener ahí una forma de cerrar sesión.
Vale, ya está.
Ya se puede cerrar sesión.
Y se puede meter...
Abrir sesión.
Ojo, ¿qué ha pasado aquí?
Real man to do handle response body error.
Hostia.
Puede ser...
Hostia, hostia.
Pues a ver, que habíamos dejado el inicio de sesión funcionando.
Y ahora no ha funcionado.
A ver si esto también pasa en producción o esto solo ha pasado...
Nos está pasando aquí.
Nos está pasando aquí o también pasa en producción.
Pronósticos.
A ver.
Claro, aquí es que no tenemos lo de cerrar sesión todavía.
No tenemos lo de cerrar sesión.
A ver, puede ser...
Puede ser...
Invalid request.
Invalid client secret.
Puede ser.
¿Cómo que Invalid client secret?
O sea, puede ser que no esté bien puesto el secreto.
Hostia, ahora me extraña esto, eh.
A ver.
Enf.demo.local.
No, punto local.
Esto está bien.
A ver, voy a mirar en Vercell un momento.
Me voy a copiar el mismo.
Porque puede ser que me...
No, la secret...
El client secret no caduca.
No caduca, no caduca.
Puede ser una tontería.
Muéstralo para corroborar.
O sea, ¿qué ratas sois que me queréis hackear?
Vosotros lo que me queréis hackear.
A ver, me voy a copiar el environment.
Que debe ser...
Algo de que estás usando el localhost.
No, debe ser una tontería.
O sea, debe ser que a lo mejor he puesto mal el secret.
O sea, cuando inicié sesión tenía un secret y luego he tenido otro y no lo actualicé.
No sé, no sé qué puede ser.
No sé qué puede ser.
Vamos a ver si me funcionan las que están.
Le faltan comillas.
Pero si no lo estáis viendo.
¿Cómo lo vas a ver si le faltan comillas o no?
Vale, vale.
Aquí me está diciendo esto.
Client ID.
Client ID.
Esto está bien.
Y este es secret.
¡Amigo!
Ya he visto el problema.
El tema es que ese secret ya no existía.
Porque lo tuve que quitar, ¿no?
¿Lo habéis visto con el reflejo?
¿Lo has visto?
¿Lo has visto?
¡Ay, que te doy!
Como me hackees, te busco y todo en tu casa, ¿eh?
Te voy a buscar en tu casa y me voy a comer a tu perro.
Como me hackees.
El tema que vamos a...
¡Ah, no!
¡No!
¡No!
Dios mío, no me lo puedo creer.
¡No me lo puedo creer!
¡No me lo puedo creer!
Dios mío, no me lo puedo creer.
No me lo puedo creer.
No me lo puedo creer.
Tanta broma, tanta hostia.
¡Ay, señor!
Perdonad.
Estoy generando un nuevo secreto.
Efectivamente.
Soy yo.
Soy esa persona.
Esa persona que se pone a hacer bromas.
Y de repente...
Y de repente, pues, muestra todos los secretos.
Los muestra a todos.
¿No me jodas?
¡Joder!
Otra vez, ¿no?
Otra vez, ¿no?
Vale, no pasa nada.
Lo bueno es que se puede generar tantas infinitas...
Tantas veces como una persona como yo necesite.
Ya vimos todo.
No cambiaste escena.
Gracias.
Gracias por avisarme.
Eres buena persona.
Gracias.
Bueno, ahora sí.
Ahora no...
La madre que me parió.
Ahora sí, ahora sí, ahora sí.
Tened paciencia conmigo.
Tened paciencia conmigo que no he merendado.
Tened paciencia.
Venga, que ahora ya sí.
Ahora sí, ahora sí.
Venga, editar...
Ahora sí que no lo estáis viendo, ¿no?
¿Verdad?
¿Verdad?
Ay, bueno, a ver ahora.
A ver ahora.
La madre que me trajo.
A ver, voy a probar si ahora funciona todo.
PNPM RANDEP.
Voy a asegurarme que no tengo abierto ningún sitio, ningún secreto.
Vale.
Perdonad, ¿eh?
Perdonad.
A ver ahora.
Pronóstico.
Se supone que ahora estoy utilizando bien el Client Secret.
Por favor.
Vale.
Ahora ya está, ¿eh?
Midu sudando.
Sí, sí, sí.
Se ve en la URL.
Sí, hombre.
Ay, Dios mío.
Gracias por la Secret Kiss.
La madre.
Un clásico de Midu.
Sí, totalmente.
El Cloak no funciona.
No funciona el Cloak, Martín Acosta.
Porque el Cloak tiene un microsegundo que se ven los secretos.
Es un rollo.
Vale, pero ahora, ahora, ahora sí.
Ahora sí.
Ahora sí que...
Vale, ahora sí.
Ahora que sí que está, ahora vamos con el resto.
Vamos con el resto.
Ya hemos añadido una forma de cerrar sesión.
Vamos a añadir, ahora sí, la base de datos.
Que tampoco es que sea algo muy complicado, pero la vamos a hacer.
Vamos a utilizar la de AstroDB, la base de datos de Astro.
Porque creo que es la más sencilla que podemos añadir.
Tiene una integración buenísima.
Y la vamos a utilizar, que creo que puede estar bastante bien.
Así que, ¿vamos a leer la documentación?
No vamos a leer la documentación.
Vamos directamente a ejecutar el comando.
Y luego vamos a ver lo que hace.
Vamos a ver.
npx-astro-add-db.
Ya sabéis que Astro tiene un comando en el que básicamente te permite muy fácilmente añadir, pues, dependencias como React y tal.
Y la base de datos, pues, no iba a ser menos.
npx-astro-add-db.
Y esto, aparte de añadir la dependencia que necesita, ¿ves?
Que es esta, astro-js-db y tal.
Lo que va a hacer es cambiar la configuración y añadirte los archivos que necesites.
Así que vamos a ver.
Astro, vale, va a crear dos archivos.
Vale.
Y todo esto, ¿qué está modificando?
Vale, ha importado esto.
Y en las integraciones, perfecto.
Pues le decimos que sí, que lo queremos todo.
Y ya tenemos la base de datos.
Ya está.
Ya tenemos la base de datos.
Ya tenemos la base de datos.
Obviamente, a ver, no es que la tenemos todavía, pero pronto, pronto.
¿Qué necesitamos por aquí?
Hostia, ¿qué es esto?
Por bien podemos hacer...
Hostia, he creado ahí un markdown ahí en medio.
No sé para qué.
Vale, esto lo quitamos.
Vale, nos ha creado una carpeta db, de database.
Y aquí tenemos dos archivos.
Config.ts y sit.ts.
O set.
No sé si es set o sit.
Creo que es sit.
Sit.
Es sit.
Y lo que hace es que esto sea la semilla por si queremos generar, pues, código, o sea,
elementos que tenga la tabla desde el principio.
Y la configuración sea la configuración de nuestra base de datos.
Un safe call of any type value.
Me da la sensación que eso también es de esto.
De la extensión.
Es que a veces, cuando instalan la dependencia...
¿Ves?
Ahora sí que funciona correctamente y no tiene ese problema.
¿Vale?
Y luego también en Astroconfig, aquí, fíjate que se me queja el formateo.
Vamos a quitar esto.
¿Vale?
Esto.
Aquí, aquí.
Esto más arriba.
Pues, total.
Helper Imports.
Esto lo podemos poner aquí.
¿Vale?
Y ya se ordena perfectamente.
¿Vale?
Pues, en la configuración, a ver, la base de datos que necesitamos la vamos a intentar mantener lo más sencilla posible
porque se cobra.
Se cobra por las lecturas y todo esto.
Y entonces, las tablas que vamos a tener.
Vamos a definir aquí arriba la tabla que le vamos a llamar Votos.
Y vamos a decirle las columnas que tiene la tabla Votos.
Vamos a hacer que la ID, la ID va a ser en las columnas.
Vamos a importar aquí columna, column.
¿Vale?
Y en column le vamos a decir que es del tipo texto.
Pero este va a ser el primary key.
¿Vale?
Le decimos true.
La ID podría ser numérica, podría ser de diferentes formas.
Pero, para aprovechar que la ID es única, vamos a crear aquí como un combo User ID y Combat ID.
¿Vale?
Para que así nos aseguremos que solo puede votar una vez.
Y que además, si intenta votar una vez el User ID, el mismo combate,
lo que vamos a hacer es que en lugar de insertar, lo vamos a actualizar.
¿Vale?
Y vais a ver que en lugar de hacer un select para ver si ya la había creado,
lo que vamos a hacer es un Upsert.
Que eso está bastante interesante.
Entonces, más cositas que vamos a necesitar.
Bueno, el Combat ID, que en este caso es un column text.
Es una cadena de texto.
Los tenemos aquí.
¿Ves?
Aquí tenemos la cadena de texto con la ID.
Pues esta sería el Combat ID.
Aquí tendríamos también, pues, el User ID.
Que User ID es raro porque no lo tengo.
Lo podemos generar nosotros a partir de algún dato que tengamos del usuario.
Pero es verdad que no lo devuelve.
No sé si es un error de AstroAuth que no devuelve el identificador,
que creo que sería lo mejor, pero no lo devuelve.
Entonces, nosotros por ahora no lo usaremos.
Usaremos a lo mejor o el email o el nombre de usuario.
Pero claro, son cosas que pueden cambiar.
No sé, si alguien encuentra en la documentación de AstroAuth que puedes hacer que devuelva el ID,
que me lo cuente y lo añadiremos correctamente.
El Vot ID, básicamente para saber a quién está votando, si está votando a un usuario o otro.
Esto sería de los boxeadores, pues, la ID, ¿vale?
Tenemos aquí, por ejemplo, pues, el Mariana o lo que sea.
Y luego, finalmente, podemos decirle a qué hora lo ha votado.
O sea que, bueno, para saber por lo que sea.
Esto a veces es interesante para tenerlo.
Así que User ID, Combat ID, Vote ID, Votat.
Con esta información, yo creo que tendremos suficiente.
Lo que tendremos que hacer es evitar que un mismo usuario pueda votar tantas veces como le dé la gana.
Pero con la generación esta de la ID, yo creo que ya no funcionará.
Como no necesitamos por ahora tener referencias y otras columnas, otras tablas,
que eso veremos más adelante si lo necesitamos.
Por ahora vamos a empezar con esta, que ya nos generará lo suficiente para poder tener un endpoint
y que la gente pueda votar y ya estaría, ¿eh?
Ya que parece que sabes de todo.
¿En verdad que te gustaría saber ahora?
No, hombre, no sé de todo.
Es más, lo que me falta por saber que lo que sé, ¿eh?
Totalmente.
Así que, ¿qué es lo que más me gustaría saber?
Bueno, a lo mejor el tema de Machine Learning.
A lo mejor Machine Learning podría estar bien.
Vale, ahora que ya tenemos esto, al menos, vamos a poner...
Vamos con la API.
Vamos con la API.
Porque en Source, vamos aquí a las páginas, API.
Aquí ya tenemos unas APIs creadas que creo que las hizo Joel,
pero que nunca terminamos de utilizarlas.
Por ejemplo, esta.
Esta no es necesaria, así que esta yo creo que nos la vamos a petar.
Si no, ya la recuperaremos, no pasa nada.
Y esta de combates también nos la vamos a petar porque tampoco es necesaria.
Y esta de forecast sí que puede ser que sea necesaria, así que esa la vamos a dejar.
Y vamos a crear una que, a ver, pues es que hay diferentes formas de hacer esto.
Voy a hacer vote y que sea combatid.ts, ¿vale?
Y así, pues haremos un post a este endpoint pasándole directamente qué combate es el que estamos votando.
Ahora, para crear un endpoint en Astro, pues tenemos que exportar una constante con el método,
la acción que queremos hacer, el verbo de acción.
En este caso, queremos que sea en post.
Para que tenga los tipos, va a decir que esto es una ruta de API.
¿Vale?
Lo importa de Astro.
Y aquí ya tenemos async.
Y lo bueno es que ya nos va a decir aquí, bueno, pues tendríamos...
Oye, ¿por qué?
A ver.
Que no me está haciendo el autocomplete esto.
Pero creo que tenemos los parámetros, ahora sí, y la request.
Pues con estas dos cositas ya tendríamos lo suficiente.
Y ya se me queja de que, oye, esto debería devolver algo.
Pues es que lo mínimo que tenemos que hacer aquí es devolver una respuesta así.
Esto sería lo mínimo que necesitamos.
Lo que...
Claro, lo que pasa es que no estamos utilizando la wait, por eso se queja.
Vale, pero esto sería lo mínimo que necesitamos para tener nuestro endpoint.
Entonces, ahora cuando hagamos un post a esta ruta, que será la ruta que vamos a hacer,
sería barra api barra bot barra el combat ID, que en este caso sería este.
Cuando hagamos un post aquí, pues contestaría esta respuesta.
Para no ir probando cada tres, yo voy a implementar bastante de esto para que veamos si funciona o no funciona
antes de estar ahí todo el rato probando cada vez, porque si no vamos a estar aquí todo el día.
Lo primero que vamos a querer probar es si tenemos usuarios.
Bueno, solo pueden botar los usuarios.
Así que vamos a importar el getSession, ¿vale?
De Authastro.
Y este getSession lo vamos a traer para aquí.
Y vamos a traernos la sesión.
Ahora sí, vamos a entrar en la wait.
GetSession y le pasamos la request.
¿Por qué la request?
Porque el getSession lo que hace es mirar las cookies y las cabeceras de la petición para saber si tiene una sesión el usuario.
Ahora, si no tenemos una sesión o no hay un session user, si el session user es undefined, vamos a decirle return new response que no está autorizado.
Bueno, a ver, para simplificar esto un poco, vamos a crear aquí una función respuesta, ¿vale?
Que le vamos a pasar, podríamos pasarle el body, que puede ser string, el status, que sea el number, y el status, no sé si pasarle status text.
Bueno, status text, vale, status text, es que no sé si pasarlo como, va, status number, status text, string.
Ya luego nos preocupamos a ver si necesitamos cambiarlo.
Básicamente para no estar repitiendo todo el rato, return, new, respond, no sé qué, no sé cuánto, ¿vale?
Entonces ya hacemos esto.
Venga, va, lo vamos a hacer así porque si no voy a estar aquí todo el día, luego con los parámetros y me va a molestar, ¿vale?
Status, status text y headers, ¿vale?
Y los headers, headers, por ahora voy a poner un any.
Oh, ¿se puede pasar un headers?
No sé si tiene... Ah, qué lástima.
Oh, sí.
¿Puede ser?
Sí, puede ser, que lo tenga.
Ah, sí, sí que lo tiene, perfecto, perfecto.
A ver, esto...
Vale, pues ya le pasamos así todo y así tengo la respuesta directamente.
Lo que tenemos que hacerle es que tanto el status como el status text como el headers, que sea todo opcional porque puede no pasársele.
Entonces ya teníamos esto, que no se ha autorizado 401 y seguiríamos ahora otra vez pues mirando constantemente.
Vamos a ver aquí console.log session.user para ver si esto tiene sesión si llega a este punto y seguiríamos viendo ahora otras cositas.
Al ser un graphic de auth.js, debería ser session.user.account.
Es que no está, no está.
Ahora os enseñaré el objeto, pero yo estoy bastante seguro que no está.
¿Por qué tira un error el console?
El console no es que tira un error, sino que te avisa el slint para que no lo dejes ahí.
Porque normalmente en la consola no quieres que utilizar la consola para llevar información y enseñarse al usuario.
Entonces, con esto, la idea básicamente es que el linter te diga, oye, acuérdate que tienes la consola aquí, que la elimines después, para que no la dejes ahí en medio.
¿Qué ganamos con la función res?
Solo abreviar.
O sea, solo para no repetir new response todo el rato.
Ya está, ¿eh?
Y como lo vamos a necesitar más de una vez, pues por eso lo estamos haciendo, para no tener que estar todo el rato ahí poniéndolo.
Entonces, primero tendríamos el usuario.
¿Qué más tendríamos que mirar?
Tendríamos que mirar, bueno, aquí tendríamos que generar la idea que me estáis comentando antes.
A ver, ah, can be used with any database.
Ya, pero esto es ya generando de la base de datos.
Adaptadores.
O sea, yo quiero la idea que me debería devolver de Twitch, que ese es el tema.
¿Que por debajo utiliza esto?
Sí, pero no sé si lo que hay en medio es el que me está dando el problema de que no me está devolviendo la idea.
Que eso es lo que me está preocupando.
Eso es lo que no está bien.
Twitch profile.
No sé si el Twitch profile me parece a mí que o no tiene...
No sé si se puede ver el Twitch profile en algún sitio.
Twitch profile, Twitch profile...
Es que no me lo está dando.
Yo creo que...
Mira, inerit from omit.id.
Es que me parece que no devuelve la idea.
Me parece una cosa loca.
Vamos a seguir, no pasa nada.
Generamos la idea y luego si eso lo miro a ver cómo lo puedo hacer.
Mira, para generar la idea puedo hacer aquí una función en un momento.
GenerateId, que le pasamos el string y utilizaremos el email del usuario por ahora.
¿Vale?
Vamos a importar cripto...
O cripto no.
CreateHash de NodeCripto.
Vamos a crearle un hash a través del email para no tener el email en la base de datos.
Vamos a createHash, pasándole el string.
No, esta es el string no.
SHA, update, digues.
Vale, pues con esto ya tendríamos una ID.
Luego ya vemos a ver si podemos conseguir la buena.
¿Vale?
UserId y le ponemos generateId con el session.user.
Punto.
Y veis que aquí pone ID, pero no existe.
No es verdad.
No está.
Es que, bueno, a ver.
Lo voy a probar.
Lo voy a probar.
Pero es que estoy bastante seguro que no está.
Si está, buenísimo.
Igual.
Y me como mis palabras, ¿eh?
Pero yo juraría que no está.
¿Vale?
Email.
Y si no, session.user.name.
Punto.name.
No se puede asignar, tipo, no sé qué.
Ah, no.
Y si no, pues, claro, para que llegue a esto, vale, vamos a hacer otra cosa.
Para que no llegue a este problema, vamos a hacer punto email.
Y así ya no tenemos ese problema.
Vale.
Ya tenemos el userId y ahora luego veremos a ver cuál es el problema.
¿Vale?
UserId.
Por qué, si usar, me estáis preguntando que por qué utilizo dos aquí.
¿Vale?
Es una pregunta válida y os lo explico en un momento.
Mirad.
¿Por qué utilizar dos igualdades en lugar de tres?
Si nosotros tenemos que a es undefined, por ejemplo, y hacemos a con tres igualdades igual a null, me da false.
Pero si utilizo dos, vais a ver que me da true.
Lo bueno de utilizar dos cuando vas a comparar para null o undefined es que ya no es necesario que mires null o undefined.
¿Sabes?
Sino que directamente dices, vale, si esto, si a es null o undefined, quiero que me des true.
Y es bastante útil para eso.
¿Vale?
Solo para eso.
No para hacer esto.
Si haces esto, ya la has liado parda.
Pero para el null es útil para esto.
Si no, lo que deberías hacer es, por ejemplo, esto.
¿No?
Tendrías que hacer esto.
Y entonces ya tendrías la comparación de ambas.
Para mirar si es null o asundefined.
Vamos a poner punto y coma, si no, no le gusta.
Pero puedes simplificarlo en un momento con las dos igualdades.
¿Vale?
Y ya lo tendrías.
Mucho más fácil.
Punto pelota.
¿Ok?
Y al final con TypeScript, es que además TypeScript te lo...
Como no tienes ningún problema porque TypeScript esto te lo va a mirar, pues ya sabemos que funciona sin ningún problema.
Más cosas que necesitamos.
Vamos a importar los combates.
Vamos a importar los combates.
Y así vamos a revisar que el combate que estamos intentando votar realmente existe.
¿Vale?
Entonces vamos a sacar el combat...
Combat ID de los parámetros.
Y aquí miramos que si no tenemos combat ID, pues le decimos respuesta 400.
Bueno, aquí he puesto...
Not found, no.
Combat ID is required.
¿Vale?
Le decimos status 400.
¿Vale?
Porque está mal la request.
Y si tenemos el combat, pues vamos a sacar el combat data.
Y hacemos combates.find.
Y vamos a buscar que realmente este combate exista.
Para que la gente no intente votar un combate que no existe.
¿Vale?
Así que si no encontramos este combate, el combate no ha sido encontrado.
404.
¿Vale?
Vale.
Más cositas que podríamos hacer.
Ya tendríamos esto.
Nos faltaría sacar la información de la request.
Porque vamos a hacer un post y vamos a tener que pasarle como JSON.
Le vamos a pasar como JSON.
Le pasaremos el...
Bueno, vote ID.
Le tenemos que pasar el voto, ¿no?
Porque el user ya lo tenemos.
La información que necesitamos la tenemos aquí.
La ID la generamos.
Nosotros.
El combat ID ya lo tenemos en la URL.
El user ID ya lo tenemos de la sesión.
El voto es lo que nos faltaría saber del usuario.
Y cuando se ha votado también lo podemos generar, ¿no?
El vote at.
Ya tendríamos.
Vote at.
Esto.
New date.now.
Bueno, no.
Date now.
¿Vale?
¿Qué más tendríamos?
El user ID.
El combat ID ya lo tenemos.
De hecho, voy a poner esto.
Vote user ID.
Vote at.
Me faltaría el vote ID.
Ese es el que me falta.
Y el combat ID.
Y me falta también la ID, ¿no?
La ID que sería el...
New ID.
Que el new ID, hemos dicho, que sería así.
El user ID y el combat ID, ¿no?
User ID y combat ID.
Vale.
Este sería el voto.
Solo me falta el vote ID.
Que esto lo tenemos que sacar de la petición que vamos a hacer.
Cuando pasemos nosotros el JSON, vamos a recuperar el vote ID y tendremos ahí la cadena de texto.
¿Lo podemos hacer?
No mal, pero a ver.
Lo primero que podemos hacer básicamente es sacarlo.
Que sería del body await request.json.
El único problema de esto es esto, ¿no?
Un safe assignment of any value.
Aquí podríamos forzar y decir que el request.json, o sea, que todo esto es esto, ¿no?
Y ya está.
Y nos podríamos quedar a gustísimo.
No teníamos ningún problema.
Pero lo cierto es que esto no estaría bien del todo porque podríamos tener problemas.
Tendríamos que revisar con un if si realmente viene esta información o no.
Entonces, como tengo ganas de una cosa, supongo que conocéis muchos Zot, ¿vale?
Y si no lo conoces, yo te digo lo que es.
Zot es una forma de validar esquemas para saber si un objeto, un array o un tipo de dato, una estructura está cumpliendo un contrato.
Y lo bueno que tiene Zot es que funciona tanto en Runtime como con TypeScript te permite también tiparlo.
Entonces vas a tener los errores de compilación de TypeScript, pero también vas a tener los problemas de Runtime.
Que vas a tener que parsear y validar que realmente funcione, lo cual es súper útil.
Pues hay una alternativa a Zot que hace mucho tiempo que tengo ganas de utilizar, que se llama Valibot.
Que es una copia de Zot, pero más pequeñita.
Que ocupa un poquito menos porque el cómo tienes que utilizarlo es importando por separado cada cosa que quiere revisar.
Entonces está muy bien porque además puedes importar solo.
Si solo vas a validar emails, pues puedes importar solo el email, solo los objetos, solo el string, solo de parsear.
Y eso hace que ocupe mucho menos.
Entonces, me hace ilusión, vamos a utilizar Valibot.
¿Os parece? ¿Vale?
El tamaño importa, el tamaño importa siempre.
El tamaño importa siempre.
Si tú, por ejemplo, te metieses en la boca una hamburguesa así o una hamburguesa así, ¿qué prefieres?
Teniendo en cuenta que has gastado 5 dólares.
Pues tú quieres una hamburguesa así.
Seguro.
Seguro.
Una hamburguesa.
¿De qué queréis que estemos hablando?
¿Qué queréis?
¿Os gustan las hamburguesas gordas?
A mí me encantan.
A mí me encantan las hamburguesas.
¿Cómo?
Pero si 8 centímetros es grande.
A ver, 8 centímetros, no sé cuánto puede ser 8 centímetros de hamburguesa.
¿Cuánto puede ser 8 centímetros de hamburguesa?
¿Sí?
Más o menos.
Se queda corta, ¿eh?
8 centímetros de hamburguesa.
Yo creo que es poquito, ¿eh?
Yo creo que debería ser más grande.
Más grande.
¿Es New ID?
¿Podría llegar a generar colisiones, no?
Quizás es una chorrada lo que digo.
Claro, es que queremos que genere colisiones.
O sea, queremos evitar...
Luego lo vamos a ver.
La generación de colisiones.
Va a generar colisiones porque si un usuario intenta botar otra vez el mismo combate,
debería colisionar.
Entonces vamos a intentar hacer que colisione para evitar que lo hagan, ¿vale?
Ese es el tema.
Ese es el tema, ¿eh?
Vale.
Gracias a ti usé Valibot en proyecto y funciona súper.
Ah, pues buenísimo.
Perfecto.
Vale.
Pues venga, vamos a con Valibot.
Lo primero que vamos a hacer es aquí pnpm at Valibot.
Añadimos la dependencia de Valibot.
Y a ver, no necesitamos gran cosa de Valibot.
Vamos a importar el objeto, el string y el save parse, ¿vale?
Para parsear la información, para que valide y nos diga si ha ido bien o no ha ido bien.
Vamos a crear por ahora el esquema de la request, que es bastante sencillo.
O sea que así lo vais a entender fácil.
Vote ID, le decimos que tiene que ser un string y ya está, porque no tiene mucha historia.
Lo que sí que podríamos mirar, que esto también podría ser interesante,
es que el Vote ID tiene que ser uno de los boxeadores, ¿no?
O uno de los equipos, claro.
El Vote ID tiene que ser uno de los boxeadores o uno de los equipos.
Pues esto, por ahora no lo voy a hacer, pero lo voy a añadir porque esto es importante que lo hagamos.
Es importante que lo hagamos.
Voy a poner por aquí, voy a poner aquí,
to do validate that Vote ID is a valid vote.
No es tan importante.
¿Por qué no es tan importante un voto válido?
Porque bueno, al final no se contará el voto y ya está, no pasa nada.
Pero es verdad que es importante para que tenga algún tipo de sentido,
que realmente lo que estemos votando tenga sentido.
Podríamos crear otra tabla, podríamos crear una tabla que tenga una referencia
y podríamos crear los datos.
Lo podríamos hacer.
Ya lo pensaré.
A ver cuáles dos hacemos.
Ahora por ahora lo único que quiero es que se pueda votar.
Luego ya nos preocuparemos.
Vale, ¿qué tenemos que hacer con esto?
Lo que vamos a hacer es poner aquí el save parse.
Le decimos que utilice el vote schema y esto lo ponemos aquí.
Y aquí en lugar de tener el body tendríamos el resultado,
que el resultado nos va a decir si ha ido bien o ha ido mal.
Fíjate que ya nos está diciendo aquí que deberíamos poder sacar el vote ID.
Para sacar este resultado deberíamos tener aquí el success o el output.
¿Vale?
Entonces, si no hemos tenido un resultado óptimo, ¿vale?
Si no hemos tenido un buen resultado,
significa que nos ha hecho aquí mal la petición.
Vamos a poner aquí bad request.
Nos ha pasado mal los parámetros y por lo tanto no se puede hacer nada.
¿Vale?
Pero si ha ido bien, deberíamos aquí ser capaces del output sacar el vote ID.
Y fíjate que ya tiene el tipo string.
O sea, ya lo está haciendo correctamente.
Con esto nos estamos evitando.
Estamos como de alguna forma ya tipando nuestra request,
nuestra petición del JSON.
Y además, estamos validándolo también.
O sea, y si añadimos más campos, pues lo añadimos aquí.
Y ya lo tendríamos en un momentito.
¿Vale?
¿Vale?
Esto que le dice que esto debería estar aquí.
¿Vale?
¿Vale?
Nos faltaría aquí validar de mesa valid vote.
Que tendríamos que hacerlo.
It should be a team or boxer field from combat.
¿Vale?
¿Vale?
Entonces, tendríamos ya el voto.
Ya tendríamos todo el voto.
Vamos a insertar por fin la información en la base de datos.
A ver qué revienta aquí.
Igual revienta alguna cosa.
Para importar esto, esto está muy chulo.
Que esto es lo que está genial del tema de la base de datos con Astro.
Porque lo que hacemos para añadir de la base de datos es que podemos importar
db from astro db.
Y también podemos traer ya...
Ah, ¿no?
Sí.
¿Veis?
Ya ha detectado automáticamente que tenemos una tabla que se llama votos.
O sea, ya detecta también las tablas que hemos creado nosotros.
Entonces, para añadir información a nuestra base de datos, lo único que tenemos que decirles es, vale,
en la base de datos queremos insertar en Votes, en la tabla Votes, los valores de este voto.
Y esto tenemos que hacer una wait.
Y espérate, ¿por qué no le gusta esto?
Insert values vote.
Por si pensaba que esto era así.
Ah, no son compatibles.
Vale, porque este...
Hostia, fíjate qué chulo, ¿eh?
Qué chulo.
Fíjate qué te dice aquí.
Bueno, aquí no, porque no se lee muy bien.
Pero aquí mejor.
Dice, los tipos de propiedad Votes no son compatibles porque le he pasado un tipo number
y tendría que ser una fecha.
Y es por culpa de esto.
Que aquí deberíamos o pasarle una fecha directamente, que esto ya le va a gustar,
o si no me equivoco, también aquí...
¿Veis?
Tenemos una constante que se llama now, que también la podemos utilizar directamente.
Y si no me equivoco...
Sí, ¿veis?
También funciona.
Que esta constante lo que va a hacer es traducir esa fecha en el momento actual de este instante.
Y ya lo tendríamos con el estilo correcto.
Bueno, con esto, con esto, con todo esto, ya tenemos alguna cosita.
Vamos a probar que esto funcione, al menos para ir probando cosas.
Así que nos vamos a la página de Vote.
Y claro, ahora aquí tenemos que hacer algo, ¿no?
En el cliente para que esto funcione.
Lo primero es que necesitamos que cada botón tenga la información...
¿Veis?
Aquí tengo, claro, tengo el botón con Class, Vote Team, no sé qué, no sé cuánto.
Pero tenemos que tener la información de cada uno para, por ejemplo, aquí el combate al que se refiere.
Vamos a decir que sea el CombatData.id.
Y el DataBotId, que este sería el Slack1.
¿Vale?
Y esto mismo lo tenemos que hacer con el botón 2.
¿Vale?
Para que cuando le demos clic tengamos esta información.
Es lo bueno y lo malo de tener React.
Porque cuando tienes React o Vue o Angular, todo este tipo de cosas las tienes como dentro del estado y ya está.
Pero cuando quieres hacer el mínimo JavaScript posible, pues tienes que ir con cosas más imperativas y poner los DataAttributes y todo esto.
Pero bueno, no es muy problemático, pero es verdad que también lo podríamos haber hecho con React o con PRIAC o con alguna cosa, ¿no?
Para hacer básicamente que esta parte que es más dinámica la podamos hacer de forma más declarativa.
Pero bueno, no me parece tampoco.
Y me parece interesante también de vez en cuando hacer cosas directamente con JavaScript que tampoco se nos caen los anillos.
Vote Team.
Lo que vamos a hacer es recuperar todos los botones que tienen esta clase Vote Team, ¿vale?
Que es como para votar.
Y vamos a iterar Vote Team, ForEach y para cada... no sé cómo llamarle.
Con cada Team.
Bueno, ya que has puesto Team.
O cada... es que Team o Button.
Voy a llamarle Button, ¿no?
Que creo que tiene un poquito más de sentido.
Button.
Vale, pues cada botón.
Vamos a sacar el CombatId y el VoteId del Dataset, que es justamente lo que hemos creado.
Esto es muy interesante lo del Dataset porque...
Ah, mira.
Aquí se me queja.
No list...
No list of HTML ButtonElement.
Para que realmente lo se esté pidiendo bien.
Esto sí está interesante porque fijaos que aquí, aunque yo he añadido los atributos del Data como Data, guión, Combat, guión y D.
Porque cuando tú accedes al elemento, al Dataset, fíjate que lo tienes que extraer con CamelCase, ¿vale?
No se extrae... creo que también lo puedes extraer... creo que lo puedes extraer de las dos formas.
Puedes extraerlo así, creo, creo, no estoy seguro.
O lo puedes extraer con el nombre en CamelCase.
Entonces, si hiciésemos el GetAttribute, seguro que sería con los guiones, ¿eh?
Sería seguro con los guiones.
Data, CombatId.
Pero al utilizar el Dataset, lo puedes hacer en CamelCase y ya lo tienes perfecto.
Que es una cosa que está bastante interesante.
Entonces, cada vez que hacemos clic en uno de los botones, vamos a hacer un Fetch.
Ah, barra API, barra Boats, barra...
¿He puesto Boats o Boat?
Boat.
Vamos a llamarle Boats, que tiene un poco más de sentido, ¿vale?
Boats y aquí le pasaríamos el CombatId.
Vale, mira, ya que me lo ha puesto todo.
El método Post, los headers, como es lo que nos vamos a enviar como un JSON, lo hacemos así.
Y en el Body le vamos a enviar el VoteId.
Vale, ta, ta, ta.
Esto, como tenemos que enviarlo como una cadena de texto.
Entonces, no le enviamos directamente aquí VoteId, sino que tenemos que enviar...
Fíjate que se nos queja, ¿eh?
Se nos va a quejear.
Tenemos que crear la cadena de texto con JSON Stingify y le pasamos el objeto que vamos a enviar.
Y con este Fetch, como esto devuelve una promesa, vamos a ver que esto tenga res.ok, res.status,
para ver si esto ha funcionado bien o no ha funcionado.
Y vamos a ver hasta dónde llega esto.
A ver si realmente nuestro código peta, si no peta, a ver qué dice esto.
Vale, en la consola ya veo cosas que no me gustan.
Vale, no puede propiedad null setting on click en la intro.
Vale, esto seguramente que está cargando una cosa aquí en intro.
Vamos a ver que si tiene login, entonces que haga todo esto.
Vale, y esto mismo, que también se me queja aquí, que claro, esto puede ser null.
Vale, vamos a hacer span, login query selector y si tenemos el span, entonces vamos a hacer esto de aquí.
Vale, y así se nos quejará menos.
Vale, ahora en la consola no tenemos ningún problema.
Vamos a ver ahora si le doy a este, ¿qué?
Vale, ok.
Ha hecho ok.
Vale, bien.
¿Qué es lo que ha pasado?
Lo que ha pasado es que como usuario he intentado votar al mismo combate otra vez.
Claro, yo había votado ya a guañar y he intentado votar a la cobra.
Y ahora ya, como ya tengo el voto, fíjate que está intentando constantemente.
¿Y qué pasa?
Que hay una constraint, la idea que hemos creado se está chocando porque necesitamos que esta idea, como lo hemos dicho aquí, como era primary key, no puede estar repetida.
Esto está bien porque así lo que estamos evitando es que lo pueda intentar hacer más de una vez, que el usuario pueda tener más de un voto por combate.
Así nos aseguramos que no pueda votar más de una vez.
Entonces, ¿cuál es el problema?
El problema es que también queremos que nuestros usuarios puedan cambiar sus votos.
O sea, si ya ha votado por lo que sea a guañar y dice no, ahora quiero votar a la cobra, pues tendríamos que permitírselo.
Podríamos hacer que no, que no lo puedan permitir, que estos son tus votos y te jodes.
Pero bueno, vamos a hacer que lo puedan permitir, que puedan cambiar entre uno y otro fácil y ya está.
Lo que vamos a hacer con esto es buscar cómo hacerlo.
Más que nada porque no tengo ni idea.
Me imagino cómo es, pero no sé cómo lo hace Drizzle, que es lo que utiliza Astro.
Entonces, ¿ves? Esto es lo que estamos haciendo nosotros y lo que queremos hacer es lo que se le llama un Absert.
Un Absert es esto.
Absert, ¿qué quiere decir?
Que intentas hacer una inserción, pero si no funciona, entonces vas a hacer una actualización.
Y esto es lo que queremos.
Hay diferentes opciones.
Una, cuando hay un conflicto no hacer nada, que no es lo que queremos.
O cuando hay un conflicto, hacer una actualización, que esto es lo que vamos a querer hacer.
Y entonces veo que aquí tenemos que utilizar un conflict do update, ¿vale?
O sea que si por lo que sea falla en eso, pues hace una actualización, ¿vale?
Un conflict do update nos dice cuál es el target, ¿vale?
Pues target.
Y en el target tenemos aquí users.id, ¿vale?
Tenemos que decirle cuál es el campo en el que queremos, en el que nos queremos fijar.
En este caso es, aquí pone users.id, pero en nuestro caso es votes.id.
Y luego le tenemos que decir qué es todo lo que tiene que actualizar.
Claro, el target es el identificador que tenemos.
El identificador es la ID.
Y luego lo que le tenemos que decir es toda la información que va a actualizar.
Me pareció mágico cuando supe de su existencia.
Es bastante mágico.
Muchísimas líneas de código desaparecieron después de eso, xdd.
Sí, es que, a ver, no siempre un absert es buena idea.
Y puede ser un poco problemático en algunas cosas, ¿no?
Porque al final se está comiendo mucha información.
Y hay veces que quieres hacer un absert solo bajo ciertos supuestos.
No es algo que quieras hacer siempre.
En este caso nos simplifica mucho la vida.
Porque tendríamos que mirar primero si existe.
Hacer un select, mirar.
Tendríamos que luego entonces insertarlo o actualizarlo.
Y esto nos lo simplifica y lo hace mucho más fácil.
Y aquí puede tener sentido porque Total sí que puede actualizar su voto.
Así que, pues nada, lo vamos a hacer.
¿Y qué es lo que tiene que enviar aquí?
Pues el combat.id, el user.id, el vote.id y el vote.at.
Que esto tiene que ser con el now.
¿Vale?
Aquí otra vez, unsafe assignment of any value.
De nuevo, yo creo que esto es porque se le ha ido la olla a la extensión.
Porque no veo que haya ningún problema con esto.
¿Ves?
O sea, se le va la olla.
Se le va la olla.
Bueno, con esto lo bueno es que ya deberíamos ser capaces de volver a nuestra página y votar a quien nos dé la gana.
Entonces, vamos a votar aquí.
¿Vale?
Y ahora votamos aquí.
Y ahora votamos aquí.
¿Vale?
Y ya veis que vota y no está dando ningún error.
Igualmente, sí que podríamos tener algún error.
Y es importante que hagamos aquí un try catch.
Porque si hay algún error, el que sea, pues que hagamos un catch de este error.
Vamos a poner un console error error.
Más que nada, pues para tener en la consola, en la parte del servidor, que ha pasado en este error.
Y aquí haremos un return más que internals.
Podría poner internal server error, que es verdad.
Pero error inserting vote.
Y podríamos pasar más información incluso para darnos alguna pista a nivel de servidor.
Pero bueno, por ahora, al menos tener esto ya no funciona.
Y luego aquí, en lugar del hello world, que esto no tiene mucho sentido, podemos poner ok, que ya está bien.
Y yo creo que esto ya estaría.
Con esto ya tendríamos.
¿Vale?
Al menos ya podríamos votar.
No se ve nada, porque esto lo vamos a arreglar ahora.
Pero al menos ya podemos votar.
Ahora lo que vamos a querer es, claro, poder...
A ver, que hay algún...
Este console log...
Mira, aquí me pone en define.
La madre que parió.
Ah, ¿ves?
El id me pone en define.
Me pone en define el id.
Que no está el id.
Es que es muy fuerte.
Mira, voy a poner el id.
El user.
¿Ves?
Con el user tendríamos aquí el image.
¿Ves?
Sí que tenemos la imagen.
Claro, podría sacar esta id.
No sé.
Saco esa id.
Podría tener sentido sacar esa id.
No lo sé.
¿Cómo lo veis vosotros?
A ver qué me contáis.
Es lo único que se me ocurre.
Pero no tiene id.
Aquí está lo que se tiene que sobreescribir para obtener la configuración.
A ver.
Pero claro, esto...
AuthProvider.
Authorization.
In most cases, you will need only to specify client ID.
¿Vale?
¿Vale?
¿Vale?
¿Vale?
¿Vale?
¿Vale?
¿Vale?
¿Vale?
AuthProvider.
AuthProvider.
All right.
Any defaults.
You can add them to provide the function.
¿Vale?
Profile.
Profile.
Autorización.
OpenID.
Ejemplo de profile.
Callback.
Que devuelve el id.
No sé qué.
No sé cuántos.
No.
Quantum linking.
¿Sí?
O sea, entiendo.
O sea, quieres decir que aquí...
Lo dudo, ¿eh?
Lo dudo.
Lo dudo, pero bueno, si...
Si me decís que lo pruebe...
O sea, se supone...
Se supone que aquí...
Esto entra aquí en Provider.
O sea, un profile.
Console.log.
Profile.
Is the return on a unit?
The only truly required fill is ID.
Pero ¿ves?
Es que no.
Es que ese no es el que devuelve.
Es que ese no es el que devuelve.
A ver, voy a poner aquí profile.
Profile.
Voy a ver si esto hace algo realmente.
Pero es que ¿ves?
Me da la sensación que esto no hace un coño.
Me da la sensación que eso no funciona.
A ver.
Es que ni siquiera pasa por ahí.
A ver si cierro la sesión.
E inicio la sesión.
Es que ni siquiera.
Y ahora me...
Ajá.
Me ha...
Ah, ah, ah.
Ojo.
¿Alguno de esos...
¿Alguno cree que uno de estos realmente es la ID?
Tengo dudas.
¿Creéis que algo de eso es la ID?
¿El out?
Es que lo único que se me puede ocurrir es que sea el out.
Eso no es la ID.
Ya.
No.
Ya.
Yo es que creo que no.
Es que yo creo que no.
Yo creo que no.
Esto tampoco.
¿El sub?
¿Puede ser el sub?
¿El sub?
¿Creéis que sí?
¿Sí?
No, ninguno es.
Es que no sé qué será el sub.
No tengo ni idea.
Es que me da la sensación que esto es para generar más el JSON Web Token que no...
Pero debería tener un campo ID.
Claro.
Es que el campo ID...
¿Veis que aquí aparecen como unos candados?
Eso podría ser.
Eso podría ser.
Voy a volver a...
Que también es raro que aquí me está petando.
Claro, podría haber...
¿Qué podría hacer?
Podría haber como en el astro out este...
Entiendo que aquí en algún sitio...
A ver, API...
No, aquí no.
Pero en algún sitio deberíamos ver como es el provider por defecto.
Next out GitHub Twitch.
En algún sitio deberíamos ver como es el provider por defecto.
Como lo hace.
Como sacan Twitch.
Pero ¿veis?
Aquí saca el sub, prefered username, email y picture.
Vale.
User, ID, client.
Aquí vemos el token.
Hostia, pero ¿por qué no en el profile no saca...?
¿De dónde está sacando el profile?
Porque me gustaría ver este método por defecto.
¿Sabes?
Me gustaría ver ese método por defecto.
Para saber cuál es la ID que se supone que está devolviendo.
Y si no...
Ver cómo lo podríamos sacar.
Twitch no te da la ID.
Te da el username.
Y después tienes que sacar la ID de una request a la API.
Es que claro, no tiene sentido.
No tiene sentido que hagamos eso.
Es que no tiene sentido.
El correo es único.
Podrías usarlo como ID.
Pero el correo Nefarian lo pueden cambiar.
Que ya he dicho que si no a las malas puedo hacer eso.
Es el outmeed, o el ID del cliente de la aplicación.
No, pero eso...
Es cosa de Twitch.
Con otros providers hay que te devuelve la ID.
Joder, maldito Twitch.
Maldito.
Maldito Twitch.
A ver.
Alguno de esos tiene que ser.
Alguno de esos tiene que ser.
A ver.
Ah, mira.
Out the client ID.
Vale.
El out no es.
Sub.
The ID of the user.
Claro, pero es que este puede ser mi ID.
Claro.
Es que ¿sabéis qué pasa con todo esto?
Es que el sub es mi ID.
No la ID del que se ha iniciado sesión.
No.
La ID de mi usuario.
El mío.
Me parece que es eso.
Hombre, aunque pone la ID del usuario que ha autorizado la aplicación.
O sea, que podría ser...
Podría ser otra cosa.
Podría ser otra cosa.
Sí, claro.
Lo que pasa es que...
No.
Del que ha iniciado sesión.
Bueno, a ver.
Lo podemos intentar.
Y lo probáis y me decís.
A ver.
Y me lo decís y ya está.
Crea un use ID para Astro.
No, pero es que ese no es el punto.
No es el punto.
No es el punto.
Igualmente, porque aquí en el profile me dice...
Refere the full profile, default, tu ID, email...
O sea, se supone que aquí habría que devolver la ID, ¿vale?
Que sería profile.sub, en este caso.
El name, el email y el image, ¿no?
Esto sería como lo mínimo que necesita para funcionar.
Pero es que es raro, ¿no?
¿No os parece raro que no lo haga ya por defecto?
¿Vale?
Entonces, ahora funcionar funciona.
Y se supone que si vamos aquí al combate y hago una votación...
¿A quién user?
No tiene la idea.
Es que la quita.
Es que la está quitando.
Es que no sé.
Creo que es algo interno, amigos.
Creo que es algo interno.
Me da la sensación...
Me da la sensación que es Outastro.
En algún punto, este proyecto se está quitando la ID.
En algún punto se está quitando la ID.
Estoy ahora casi seguro al...
Bastante.
Estoy bastante seguro.
Bastante seguro que es culpa de este proyecto.
A ver, culpa no.
Pero que hay algo aquí que hace que no devuelva la ID...
Estoy un poco convencido.
Porque ya veis que internamente parece que sí que la está devolviendo Provider.
No sé qué, no sé cuánto.
Prefix...
Creo que en algún punto hay...
En algún punto hay aquí que saca la ID.
A lo mejor la desestructura.
Vete a saber para qué.
Y entonces, lo que ocurre con eso es que entonces ya no la tenemos en todos los sitios.
No tiene ninguna issue sobre eso.
Puede ser.
A ver.
Discord Provider...
Is missing user id in the session object?
¿Ves?
Tengo el mismo error en GitHub.
La solución es añadir un callback dentro del Out Properties.
La solución es añadir no sé qué, no sé cuánto.
Y aquí le peta.
No funciona para mí.
He añadido...
Joder, todo lo que he añadido.
Callback session, no sé qué, no sé cuánto.
O sea, es un problema...
O sea, yo estoy bastante seguro que esto no debería funcionar así.
Creo que hay algún error ahí que obliga a que tengas que hacer esto.
A ver, vamos a probar.
A ver si con esto funciona.
Callbacks...
Ah, esto, callbacks.
Tiene que estar fuera del Provider.
O sea, esto lo quitaríamos.
¿Vale?
Y esto lo pondríamos aquí.
Esto, la sesión, la tendríamos aquí.
Callbacks...
Debería ir aquí, ¿no?
Vale.
Si la sesión...
Es que es raro que este código funcione.
Este código está mal.
O sea, este código está mal.
Sesión, este if, aquí, falta una llave.
Bueno, vamos a ver.
Si sesión tiene usuario, mete el user id del user id y tal.
Esto no tiene buena pinta.
Esto no tiene buena pinta.
Pero bueno, vamos a ver.
Vamos a ver si funciona.
A ver si funciona.
Si funciona, maravilloso.
Si funciona, todos contentos.
Vale, iniciar sesión.
Vale.
Ni siquiera me ha iniciado sesión la madre de Copario.
Ni siquiera ha iniciado sesión.
Por culpa del callback este.
Porque antes sí que me estaba funcionando.
A ver.
¿Ves?
O sea, es culpa del callback.
Este callback hace que no me inicie la sesión.
Que raro, ¿no?
Porque al final, ¿qué problema tiene de que devuelva aquí el user este?
No debería tener ningún problema, ¿no?
¿Es callbacks o callback?
Claro, como lo han puesto aquí, era callbacks.
Pero es verdad que tiene razón.
Que no sé si...
Aquí pone que es callbacks.
Callbacks.
La solución es añadir tal.
Dice, para mí he tenido que añadir esto a la auth config.
No para Discord.
Solo lo he probado con Giham.
No para Discord.
Pero ya veis que aquí el problema que le veo es que no...
El callback pocho.
Es que además no me está devolviendo...
Debe ser que no devuelve exactamente lo mismo.
Porque ya veis que ahora no me inicia la sesión.
Ahora no me está devolviendo la sesión correctamente.
Y me imagino que van por ahí los tiros.
De ese callback estamos haciendo ahí algo que no debería.
Vale.
Bueno, aquí pone también que hay un non-click.
Voy a arreglar eso un momento, ¿vale?
Para que no se me queje tampoco de esto.
Logout.
Logout.
Vale.
Si no tenemos el logout...
Bueno.
Si tengo logout, entonces hacemos esto.
¿Vale?
¿Y si mejor la sacas de la imagen con esta regex?
Podría.
Es en singular.
Vale, vale.
Pues lo hacemos.
El problema...
Pero es raro.
Porque si es en singular, ¿por qué no funciona igualmente?
O sea, debería funcionar todo igual, ¿no?
¿No es raro?
Venga, pongo callback.
Ah, mira.
Ahora sí que ha funcionado.
Chin, chin, chin.
Vale.
Vamos a poner console.log.user.id.
Y cerrar sesión.
Iniciar sesión.
Ni siquiera está entrando aquí.
Ni siquiera está entrando aquí.
Y a ver aquí, si votamos...
Tampoco está.
No está la idea.
Tampoco.
No está la idea.
Yo creo que no...
Es que, ¿sabes qué pasa?
Que yo no tengo claro que esto sea la idea del usuario.
Esto a lo mejor es la idea de esa imagen.
¿Sabes?
Que tampoco te puedes fiar de eso.
Tampoco te puedes fiar de eso.
Yo creo que tampoco.
Tampoco está haciendo eso con el...
¿Por qué user es undefined?
Cuando no tienes iniciada la sesión, ya lo tendrías.
O sea, ya tendrías que sea undefined.
Bueno, total.
Que aquí, este use...
Es que ni siquiera entra aquí.
Ni siquiera está entrando aquí.
A ver, define config.
A ver, si miramos aquí el autocomplete.
Es callbacks.
O sea, es en plural.
Eso no hay ninguna duda.
Porque ya os digo...
Ah, mira.
Ahora ha sido undefined.
Pero, ¿veis?
Me entra aquí.
Y entonces ya no entra.
¿Ves?
Aquí es que está haciendo que user sea undefined.
Si user, no sé qué, no sé cuánto, es undefined, entonces...
Y si no...
O sea, claro.
Si no, devuelve esto.
Pero es que devuelve la sesión.
Sesión.
Esto no está bien.
Aquí este callback tiene algo que no está bien del todo.
Callback, sesión, out, next out.
Aquí hay algo que no está bien del todo.
Que no está bien del todo.
Callbacks.
Claro, aquí hay singin, redirect, sesión.
Y pone que por defecto la sesión...
Se supone que este debería ser el que es por defecto.
Devolver la sesión tal cual.
Y con esto debería funcionar exactamente igual.
¿Vale?
Y ahí podemos ver que es verdad.
Que funciona exactamente igual.
Si le cambiamos el user, ¿ves?
Ya me pone aquí que el user es undefined.
Lo cual ahí me sorprende que siempre sea undefined.
Inicio la sesión.
Y es que me está dando undefined todo el rato.
Lo cual es raro.
Es raro.
Sesión.
Y la sesión sí que entiendo que tendremos aquí la información.
Pero es otra vez la misma información sin la ID, ¿sabes?
Entonces, nada.
Lo vamos a dar no por imposible, porque obviamente no es imposible.
Pero hay algo raro que está haciendo Azaustro.
Bueno, que igual es lo que decís.
Que Twitch a lo mejor no lo da.
Puede ser.
Puede ser que no lo dé.
Puede ser que sea un problema de Twitch y ya está.
Que en otro sí que lo pueda estar haciendo y ya está.
El problema es que no puedes usar el ID que te venga de Twitch.
Porque ¿qué pasaría si permitieras dos modos de login?
En Twitch puedes tener una ID.
Bueno, pero solo vamos a permitir un logo.
¿Por qué no cambia la imagen y prueba si te da el mismo ID?
Es que estoy seguro.
Estoy seguro que si no te dan la ID fácil, no te la van a poner la imagen.
¿Sabes?
Sería muy raro.
Sería muy raro.
Luego lo miramos.
Por ahora vamos a hacer que esto funcione y ya está.
Y ya está.
Podemos utilizar...
Bueno, ya estoy utilizando el email como ID.
Estoy generando aquí.
Generate ID.
Session.user.email.
Y ya está.
Session.user.email.
Con el email, pues ya tendríamos ahí una posibilidad y ya está.
Vale.
Con esto ya tendríamos todo lo que son los votos.
Ahora lo que tengo que hacer, que con todo este rollo hemos estado un rato, vamos a sacar los votos de la base de datos para el usuario.
Vale.
Así que vamos a import votes de ver from astro de ver.
Vale.
Y hacemos aquí una petición para sacar los votos.
Bueno, vamos a...
Mira, tenemos que hacer user ID.
Y el user ID, otra vez, de nuevo, voy a crear aquí una librería, porque si no vamos a estar aquí repitiendo código innecesariamente.
Vamos a poner aquí users.ts, vale, combat ID.
Aquí que habíamos creado el generate ID, voy a sacarlo por acá.
Vamos a poner esto por aquí, exportar.
Esto lo quitamos, esto lo quitamos, lo movemos aquí.
Más que nada, para generate ID, aquí esto lo podríamos hacer desde el user.
User, user, y así user.email.
Y si user no tiene email, entonces hacemos un throw, new error.
User email is required.
Vale.
Entonces, ya tendríamos el generate user ID para utilizar el email.
Este generate user ID lo vamos a importar aquí.
import generate user ID.
Y aquí que habíamos hecho el generate user ID, pues ya lo tenemos por acá.
Y lo que hay que pasar ahora es el usuario directamente, para que internamente ya mire todo lo que tenga que hacer, ¿vale?
generate user ID, sesión.user.
Y este generate user ID lo vamos a importar from lib users, ¿vale?
Vale.
Tendríamos la sesión, ah, el usuario que ya lo está recibiendo por parámetros.
No se puede utilizar antes de, vale, porque lo tenemos que tener aquí abajo, ¿vale?
Aquí tendríamos el usuario y vamos a sacar los votos del usuario actual.
DB.select from votes.
Hay que pasarle la tabla que importamos, ¿vale?
No hay que pasarle con un string, que si no...
Y le decimos donde, no sé si es así, ¿vale?
El where, creo que había que utilizar, creo que hay que utilizar un método que se llama equal, aquí, ¿ves?
Import, equal, vale.
Pues importamos este método, equal, votes, porque me ha separado esto.
Equal.
Esto por aquí.
Y aquí en el where le decimos donde sea igual el...
Vale.
Le decimos votes.userID sea igual al userID.
Y con esto ya deberíamos tener los votos del usuario.
Si hacemos un console.log de votes.
Vale.
Ahí tenemos los votos del usuario.
¡Hostia!
¿Y esta ID que ha metido aquí?
¡Ah!
Porque es el hash del usuario.
Es verdad, joder, vaya pedazo de ID.
A ver, no pasa nada, no pasa nada.
Porque, claro, el userID como es un hash, pues claro, es bastante largo.
Vale, pero ahí tenemos los votos.
Vale.
Para simplificar esto y no tener que hacer un find en cada uno de los votos,
lo que vamos a hacer es...
Es una pena que no tengamos el groupby.
Porque aquí el groupby sería la hostia.
Aquí el groupby sería la bomba.
A ver, aquí está el object.groupby.
No sé si está.
No, aquí no está.
Lástima.
Pero ahí nos serviría un montón.
Si no, lo que podemos hacer es básicamente userVotes y para cada voto podríamos hacerle voto.
Y dentro del userVotes vamos a poner el vote.combatID y dentro voteID.
Porque es la única información que necesitamos.
De hecho, podríamos sacar eso.
¿Cómo podríamos sacar el select?
A ver, select, select.
Y de...
Vale.
Pues vamos a sacar aquí el combatID que sería votes.combatID.
Y el voteID que sería votes.voteID.
Creo que con esto ya lo tendríamos.
Y aquí...
A ver qué dice aquí.
UserVotes es de un tipo any.
Bueno, no pasa nada.
Eso luego ya lo arreglaremos.
Porque claro, se está quejando porque esto al final le estoy metiendo aquí cualquier cosa
y se lo está comiendo con patatas.
Pero ahora lo que quiero ver es que en los votos tengamos realmente la estructura como quiero.
Vamos a verlo.
Vale.
Votes.
Vale, ¿veis?
Ahora combatID, combatID, voteID.
Ah, no, claro.
Es que estaba alucinando.
Digo, ¿pero cómo?
No tiene sentido.
No, no.
Esto es lo que quiero.
Esto es lo que quiero.
Lo que quiero es básicamente tener la key de cada combate y el valor del voto.
Y esto es que me va a simplificar mucho la vida.
Que por cierto, esto lo podríamos hacer con un reduce.
Si seguro que hay alguien que está apretando los dientes en su casa diciendo
Pero Midu, esto lo podrías haber hecho con un reduce.
¿Por qué no lo hiciste?
¿Por qué no lo hiciste con un reduce?
Bueno, pues mira, lo podrías hacer así con un reduce.
¿Vale?
Podrías hacerlo así con un reduce si quieres.
Si te gusta más, lo puedes hacer así con un reduce.
A mí personalmente me gusta menos.
Sinceramente.
Pero bueno, si tú eres feliz, hazlo con el reduce este y ya está.
Pero yo me gusta más esto.
Más fácil de entender y menos problemas para el mundo.
¿Vale?
Entonces, ahora que tenemos esto, lo que ya tenemos que hacer aquí...
¡Buah!
Aquí vamos a tener que hacer unos cuantos cambios, ¿eh?
Aquí vamos a poner una lista.
¿Vale?
Una lista...
Y a ver cómo separamos esto.
Aquí tendríamos que tener que...
Vamos a ponerle isVoted en el caso de que userVote.
O sea, tenemos que mirar que los votos de este combate...
Y aquí tenemos la id.
CombatData...
CombatDataId sea igual a este boxeador, a este equipo, ¿no?
¿Vale?
Esto por un lado.
Y lo mismo tendríamos que hacer...
De hecho, me lo voy a copiar exactamente igual.
De hecho, esto deberíamos sacar también en un componente, pero es que...
Vamos a por todas.
¿Vale?
Esto, lo que tendríamos que hacer aquí es que fuera el segundo.
¿Vale?
Vale.
UserVotes...
Tu, tu, tu, tu, tu...
UserVotes...
Vamos a poner aquí para que me deje tranquilo.
String, string...
¿Vale?
Y que no me esté molestando el rato TypeScript.
¿Vale?
Y para...
Claro, ahora tendremos que hacer...
Ahora tenemos una clase que le dice si está votado.
¿Vale?
Vale.
Vamos a simplificar alguna cosita por aquí.
Voting.
Vamos a poner el span.
Aquí.
Esto lo vamos a meter aquí dentro.
Pi, pi, pi, pi, pi, pi, pi, pi, pi, pi, pi, pi, pi, pi, pi, pi, pi, pi, pi, pi.
¿Vale?
Y lo que vamos a hacer aquí es detectar que si está votado, pues...
A ver.
Vamos a poner una saturación.
Vale.
Al menos para que veamos quién hemos votado.
¿Veis que estos dos están iluminados?
Estos dos están iluminados porque son los que hemos votado.
Si yo ahora le doy a este y reinicio.
¿Vale?
¿Ves que este es el que está iluminado?
De hecho, voy a poner todos los de la izquierda.
Voy a votar a todos los de la izquierda.
¿Vale?
Y veis, están todos iluminados los de la izquierda.
Si votamos a todos los de la derecha...
¿Vale?
Tú, tú, tú, tú, tú.
Están todos iluminados los de la derecha.
O sea, esto está funcionando bien.
Solo que tenemos que hacer que esto sea todavía más visible.
Para que sea más visible, vamos a cambiar el span que teníamos por aquí.
Fíjate que aquí pone ganador.
No sé qué.
Vamos a simplificar esto.
Y vamos a poner uno que sea para votar.
¿Vale?
Voy a poner un voto a este.
Luego, el tema de texto ya los arreglaremos porque tampoco podemos estar ahí.
¿Cuál es la mejor?
Y si ya lo hemos votado, vamos a poner tu voto.
¿Vale?
Y aquí vamos a estilar esto un poquito para que...
¿Cómo se debería ver esto?
A ver.
Vamos a poner el to vote text.
Vale.
Esto no es lo que queremos, pero gracias por la ayuda.
Esto por aquí y already voted.
Vale.
Por ahora vamos a hacer que tengan la opacidad a cero.
Ahora, cuando hago hover, el to vote text debería tener una opacidad.
De 100.
¿Vale?
To vote text.
Entonces, al menos esta parte...
¿Vale?
Voto a este.
Es un poco grande como ha quedado esto.
Vamos a hacerlo un poco más pequeño.
¿Vale?
Voto a este.
Y también esto es un poco que está demasiado abajo.
Vamos a poner 16.
Bueno.
Me pasé.
Vale.
Voto a este.
Ganador.
Vale.
Porque esto...
Ganador.
Ahora lo arreglamos.
Entonces, hacemos el hover.
Pero, si está votado, aquí es donde vamos a cambiar un poco esto.
¿No?
Vamos a hacer...
Si ya está votado, el to vote text le vamos a poner la opacidad cero.
Voto a este.
Ganador.
Claro.
Pero el que ya está votado debería aparecer siempre.
De hecho, lo que vamos a hacer es que esto vamos a poner que sea hidden.
Porque si ya está votado, no tiene sentido, ¿no?
O sea, ya debería...
Y el ganador.
Vamos a poner...
¿Por qué pone ganador?
Ah, porque solo lo he puesto una vez.
Solo lo he puesto una vez.
La madre que me parió.
Claro, esto tiene que ponerlo aquí y también aquí.
Por eso...
Por eso estaba viéndolo regular.
Vale, vale.
Entonces, claro, claro.
Entonces, si ya está votado, si este es el que está votado, el already voted text, este...
Vamos a ponerle la opacidad a 100.
¿Vale?
Y ahora...
Ahora sí.
Tu voto, tu voto, tu voto.
Vale, vale.
Y ahora puedo darle a voto a este.
Y aquí este encima.
Vale.
Y de hecho, deberíamos poner que...
Is voted...
Esto no sé por qué pone filter.
Saturate 100.
Vale.
Pointer events none.
Pues no tiene sentido.
Y podemos ponerle la escala.
Esto sí.
Pero...
Hostia.
Me he pasado con la escala, ¿eh?
Algo así.
Vale.
Porque tampoco tiene sentido que haga hover.
Solo puede hacer hover el otro.
¿Vale?
Hostia, este ahora, ¿por qué queda tan encima esta imagen?
Antes no quedaba tan encima.
He hecho algo ahí que me he perdido.
Bueno.
Igual podríamos hacer esto un poquito más grande.
Que lo hemos comentado antes y no lo he hecho.
¿Vale?
Pero...
Algo así.
¿Vale?
Vale.
Vale.
Voto a este.
Tu voto.
Vamos a cambiar también los colores.
Porque que los dos sean del mismo color es un poco raro.
Entonces.
Tu vote text.
Tu vote text.
Tu...
Para votar.
Text.
Action.
Vale.
Amarillo y el verde.
Vale.
Y nos faltaría la transición, ¿no?
Transition.
Opacity.
Vale.
Voto a este, voto a este.
Vale.
Ahora lo que nos faltaría es hacer también que funcione en el cliente.
Que eso es...
A ver, ¿no?
Es difícil.
De hecho, lo vamos a hacer ahora en el momento.
Un poco de espacio entre las peleas, ¿no?
No me quiero meter mucho en el estilos porque estaría todo el rato con estilos y tal.
Y me dais input constante y no avanzaríamos en ningún momento.
Entonces, como sé que luego vais a hacer PRs y tal, ya lo haremos, ¿eh?
Estaría bueno que se muestre un alert al momento de votar para darle un feedback al usuario.
Hombre, un alert no, ¿eh?
Pero algo haremos.
Un toast o alguna cosa, ¿eh?
Lo haremos.
No os preocupéis en eso.
Vale.
Voy a hacer que también funcione la UI porque si no es un poco raro, ¿no?
Vamos a hacer...
No hay diseño.
Para esta página no hay diseño.
No hay diseño.
Entonces, es un poco...
Vamos haciéndolo poco a poco.
Vamos a hacer que funcione el cliente porque el problema es que ahora cuando le doy, fijaos
que no pasa absolutamente nada.
Lo que tenemos que hacer es que en el forEach, en el forEach, aquí del onClick, cada vez
que hacemos un click, primero es, vamos a recuperar el padre, ¿vale?
El padre.
Y del botón, botón, parent, element, bueno, no sé si es HTML, voy a ponerle HTML element,
que es todo lo que necesitamos.
Entonces, del padre, lo que tendríamos que mirar, si tenemos padre, si tenemos el padre,
vamos a buscar el previous...
previousVoted, ¿vale?
Y esto que sea HTML element, ¿vale?
Sacamos el elemento previamente votado y esto, exacto.
Vamos a buscar del padre, con el querySelector, el isVoted.
Y ya lo que hacemos es, si tenemos el previousVoted, o mira, lo vamos a hacer más fácil.
Con esto, le quitamos el isVoted.
Con esto, al menos, ya quitaríamos el anterior, ¿vale?
Quitaríamos el voto anterior.
Y lo único que tendríamos que hacer aquí, pues es añadir el nuevo.
Y esto lo hace solo en el caso de que exista, porque a lo mejor no habíamos votado todavía.
Y aquí ya, en el button, classList, add isVoted.
Y esto añadiría en el nuevo.
Con esto, ahí está, ¿vale?
Ya tendríamos que cambia entre uno y otro.
Vale, pues perfecto.
Para ir añadiendo estas cositas, add database astro.
Vamos a añadir esto, esto.
¡Hostia!
Que no me he añadido.
Ah, vale, ahora sí.
Vale, add new images, add new images.
Vale, la configuración, este environment.
Vale, esto no lo hemos usado.
Create database for votes.
Vale, que no la quiero liar de que se me cuele otra variable de entorno.
Add new checks to avoid console errors.
Vale.
Vale, en users, create a library for creating ideas from email.
Vale, vamos a eliminar remove not needed APIs.
El combat ID.
Create new API for voting combat ID.
Vale, la página pronósticos.
Pass user to vote system.
Y finalmente, la de votos.
Add functionality to vote and retrieve previous votes.
Vale.
Muy bien.
Pues, pum, push.
Mañana continuaremos.
Hostia, no me digas que...
No, no.
Que tengo que hacer un pull.
¿Por qué?
Uf, menos mal.
Por un momento, por un momento pensaba que me iba a dar algún conflicto.
Vale.
Pues, por ahora, lo dejamos así.
Pero mañana continuaremos y le seguiremos dando cañita.
¿Vale?
Porque mañana tenemos que terminar a esto.
Que es lo que queremos.
¿Vale?
Si cambiaste la variable de entorno, no te da error al desplegarlo.
No, porque lo he cambiado también la...
Justamente lo he cambiado también en el...
En las variables de entorno.
Ya sabéis, PRs son bienvenidas.
¿Ok?
Las PRs son bienvenidas.
No sé si habéis hecho alguna nueva.
Pero luego las estaré revisando.
Muchas gracias a todos por estar...
Ah, mira.
No sé si habrá cambiado aquí.
Ah, mira.
Qué bonita esta.
La idea está genial.
Solo quedaría mucho mejor si la máscara fuese inclinada como estaba antes, pero en el sentido opuesto por el contraste.
He actualizado el ángulo de...
Ah, pues mira, esta está bonita.
Está bonita, ¿no?
Esta me gusta, ¿eh?
Pues eso.
Mañana continuamos con base de datos y backend.
Yo creo que va a quedar ya...
Nos queda poquito para poder hacer las predicciones.
Luego tendremos que hacer para poder compartir las predicciones por internet.
Para generar la URL, la página del usuario donde se vean sus predicciones y un montón de cosas más.