<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="es">
    <title></title>
    <link rel="self" type="application/atom+xml" href="https://juanmanzanero.com/es/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://juanmanzanero.com"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-06-30T00:00:00+00:00</updated>
    <id>https://juanmanzanero.com/es/atom.xml</id>
    <entry xml:lang="es">
        <title>Manual de Supervivencia Ante el Registro de Lineas en México</title>
        <published>2026-06-30T00:00:00+00:00</published>
        <updated>2026-06-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Juan Manzanero
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://juanmanzanero.com/es/blog/manual-de-supervivencia-ante-el-registro-de-lineas-en-mexico/"/>
        <id>https://juanmanzanero.com/es/blog/manual-de-supervivencia-ante-el-registro-de-lineas-en-mexico/</id>
        
        <content type="html" xml:base="https://juanmanzanero.com/es/blog/manual-de-supervivencia-ante-el-registro-de-lineas-en-mexico/">&lt;h2 id=&quot;esto-no-se-puede-frenar&quot;&gt;Esto no se puede frenar&lt;&#x2F;h2&gt;
&lt;p&gt;No es por ser pesimista, pero son distintos países que están enfocados en poder
identificarnos a como de lugar, ya sea al usar servicios de Internet, como al
estar en la calle.&lt;&#x2F;p&gt;
&lt;p&gt;No importa quién gobierne, o si votamos por alguien que diga oponerse, al final,
de una manera u otra, se implementarán restricciones a estos servicios, ya que
el saber cada movimiento de la gente es lo que hace al gobierno tan poderoso.&lt;&#x2F;p&gt;
&lt;p&gt;Esto que quede claro, los impuestos que nos roban no son usados para mejorar
nuestra calidad de vida, sino para quitarnos libertad.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Nota: pondré fuentes adicionales que muy probablemente estén solo en Inglés,
usa la funcionalidad de traductor de tu navegador o usa algún LLM para
traducirlo.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Dejando esto claro, veamos las opciones.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;celular&quot;&gt;Celular&lt;&#x2F;h2&gt;
&lt;p&gt;Los Google Pixel son los mejores celulares en términos de seguridad, tanto es
así que es el único hardware compatible con GrapheneOS, el Operative System más
seguro y privado con base en Android disponible para dispositivos de bolsillo.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;acceso-a-datos-moviles&quot;&gt;Acceso a datos móviles&lt;&#x2F;h2&gt;
&lt;p&gt;Cuando no tienes acceso a Internet mediante Wi-fi, usamos datos móviles para
conectarnos a las antenas de la calle, pagando a nuestro proveedor de número
telefónico (ejemplo: Telcel, Movistar, etc.).&lt;&#x2F;p&gt;
&lt;p&gt;Esto suele estar sujeto a una SIM (chip físico), pero debido a las restricciones
de esto, &lt;strong&gt;la solución es una E-SIM&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Una E-SIM te permite conectarte a la red de datos móviles, pero no todos los
proveedores te proveen de un número para recibir o realizar SMS o llamadas (daré
soluciones a esto más adelante).&lt;&#x2F;p&gt;
&lt;p&gt;Recomiendo usar algún proveedor que al menos de la opción de pagar con
Monero&#x2F;XMR, para proteger tu identidad con respecto al servicio que usarás.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;silent.link&#x2F;&quot;&gt;silent.link&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;comunicaciones&quot;&gt;Comunicaciones&lt;&#x2F;h2&gt;
&lt;p&gt;Los SMS y llamadas usando las apps por defecto de los celulares &lt;em&gt;no son
seguras&lt;&#x2F;em&gt;, ya que carecen de encriptación.&lt;&#x2F;p&gt;
&lt;p&gt;Tu proveedor puede leer y escuchar tus conversaciones (y créeme que lo hacen sin
discriminar).&lt;&#x2F;p&gt;
&lt;p&gt;Usa alguna app que tenga encriptación punto a punto (E2E).&lt;&#x2F;p&gt;
&lt;p&gt;También es importante que te permita usarla sin necesitar de un número de
teléfono, por eso descarto WhatsApp, Telegram o Signal.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;simplex.chat&#x2F;es&quot;&gt;SimpleX Chat&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Si no tienes alternativa y debes usar WhatsApp, Telegram o Signal, usa algún
servicio que te permita comprar SMS, o cuentas ya creadas.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.smspool.net&#x2F;&quot;&gt;SMSPool&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;buynumber.io&#x2F;&quot;&gt;buynumber.io&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;finanzas&quot;&gt;Finanzas&lt;&#x2F;h2&gt;
&lt;p&gt;Todos los bancos hacen obligatorio poseer un número de teléfono capaz de recibir
SMS para tokens de autenticación, según es por seguridad, pero en realidad es
para que el gobierno pueda coaccionarte en caso de ser necesario.&lt;&#x2F;p&gt;
&lt;p&gt;No hay forma de evitar necesitar un SIM para tener acceso a bancos, pero sí
puedes optar por alternativas.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Dinero en efectivo (cash)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.getmonero.org&quot;&gt;Monero&#x2F;XMR&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;El cash para transacciones en persona o comercios físicos, y Monero para
transacciones por Internet.&lt;&#x2F;p&gt;
&lt;p&gt;Lo ideal, desde luego, es usar Monero siempre, incluso hay negocios que lo
aceptan en persona, ya que es una monera totalmente fuera del sistema
tradicional.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;recursos-adicionales&quot;&gt;Recursos adicionales&lt;&#x2F;h2&gt;
&lt;p&gt;Busca más servicios que respeten tu privacidad y datos.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;monerica.com&quot;&gt;Monerica&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;kycnot.me&quot;&gt;kycnot.me&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;opbible7nans45sg33cbyeiwqmlp5fu7lklu6jd6f3mivrjeqadco5yd.onion&#x2F;&quot;&gt;The OPSEC Bible&lt;&#x2F;a&gt;  (.onion, abre este link en Tor Browser)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="es">
        <title>La Privacidad Debe Ser Tomada Más en Serio</title>
        <published>2026-04-23T00:00:00+00:00</published>
        <updated>2026-04-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Juan Manzanero
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://juanmanzanero.com/es/blog/la-privacidad-debe-ser-tomada-mas-en-serio/"/>
        <id>https://juanmanzanero.com/es/blog/la-privacidad-debe-ser-tomada-mas-en-serio/</id>
        
        <content type="html" xml:base="https://juanmanzanero.com/es/blog/la-privacidad-debe-ser-tomada-mas-en-serio/">&lt;p&gt;La privacidad es un pilar fundamental de la libertad de expresión.&lt;&#x2F;p&gt;
&lt;p&gt;Puede que hayas encontrado &lt;em&gt;propaganda&lt;&#x2F;em&gt; en redes sociales, sobre
“individuos&#x2F;grupos anónimos” están cometiendo crímenes, hackeos, etc.&lt;&#x2F;p&gt;
&lt;p&gt;Bueno, como dije, eso es pura &lt;em&gt;propaganda&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Los gobiernos quieren convencerte de que la privacidad y el anonimato significan
crimen.&lt;&#x2F;p&gt;
&lt;p&gt;No debería decirte esto, pero es una forma de pensar estúpida.&lt;&#x2F;p&gt;
&lt;p&gt;No porque un criminal use Tor o Monero significa que todos quienes usen las
tecnologías mencionadas son criminales.&lt;&#x2F;p&gt;
&lt;p&gt;Es como decir que si un criminal usa una cuchara significa que todos quienes
usen una cuchara son iguales criminales, sí; así de estúpido suena.&lt;&#x2F;p&gt;
&lt;p&gt;La privacidad es un derecho básico, garantiza que aquellos en el poder puedan
ser cuestionados sin que puedan deshacerse de sus críticos, y esto es más
requerido, ya que la censura continúa incrementando.&lt;&#x2F;p&gt;
&lt;p&gt;Puede que hoy censuren malas palabras, mañana memes del idiota en la silla al
que la gente llama presidente, en 1 mes no podrás hablar de nada más que de
buenas cosas del gobierno.&lt;&#x2F;p&gt;
&lt;p&gt;Pero como la libertad, todos la quieren, pero no todos van a luchar por ella, al
menos en estos días.&lt;&#x2F;p&gt;
&lt;p&gt;Aprende sobre &lt;em&gt;PGP&lt;&#x2F;em&gt;, &lt;em&gt;Tor&lt;&#x2F;em&gt;, &lt;em&gt;Monero&lt;&#x2F;em&gt;, y cualquier otra tecnología que te
proteja.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="es">
        <title>Website migrado a Astro</title>
        <published>2024-06-26T00:00:00+00:00</published>
        <updated>2024-06-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Juan Manzanero
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://juanmanzanero.com/es/blog/website-migrado-a-astro/"/>
        <id>https://juanmanzanero.com/es/blog/website-migrado-a-astro/</id>
        
        <content type="html" xml:base="https://juanmanzanero.com/es/blog/website-migrado-a-astro/">&lt;p&gt;Migré este website a Astro, de tal forma para probarlo, me terminó gustando.&lt;&#x2F;p&gt;
&lt;p&gt;Probé esta herramienta para construir website estáticos, ya que Vercel ha estado tomando decisiones raras sobre la dirección del desarrollo de Next.js.&lt;&#x2F;p&gt;
&lt;p&gt;Eso, y que me di cuenta que solo necesito un static website, no necesito de SSR.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;static-es-el-camino-a-seguir&quot;&gt;Static es el camino a seguir&lt;&#x2F;h2&gt;
&lt;p&gt;Al inicio del Internet, los website eran simples archivos planos de HTML, CSS y JS, no había necesidad de Single Page Application, Server Side Rendering, etc.&lt;&#x2F;p&gt;
&lt;p&gt;Por supuesto, hay muchos websites que necesitan estas estrategias de rendering, pero para un website como este, solo static es mejor.&lt;&#x2F;p&gt;
&lt;p&gt;Menos tiempo buildeando, carga rápida, hosting barato, menos JavaScript en el browser.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lo-genial-sobre-astro&quot;&gt;Lo genial sobre Astro&lt;&#x2F;h2&gt;
&lt;p&gt;Puedes usar cualquier component de cualquier framework.&lt;&#x2F;p&gt;
&lt;p&gt;Puedes usar components de React, Angular, Vue, Svelete en el mismo proyecto, ya que Astro los compila como HTML&#x2F;CSS&#x2F;JS.&lt;&#x2F;p&gt;
&lt;p&gt;No solo eso, pero puedes usar Astro como si fuera Next.js, con la estrategia de &lt;em&gt;hybrid&lt;&#x2F;em&gt; rendering, puedes especificar que páginas quieres que sean Server Side Rendered. Obviamente, si vas por este camino, debes hostear tu proyecto en un server.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;en-otras-cosas&quot;&gt;En otras cosas…&lt;&#x2F;h3&gt;
&lt;p&gt;Estoy planeando en hacer videos para YouTube, hablando de temas que considero necesitamos en la comunidad hispana, temas como cyberSecurity, privacidad, el actual estado de la tecnología, y más.&lt;&#x2F;p&gt;
&lt;p&gt;Hay muchos canales de YT en Inglés hanlando cobre eso, así que es por eso que pienso hacerlo en Español.&lt;&#x2F;p&gt;
&lt;p&gt;Aún así necesito mejorar los banners de estos posts…&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="es">
        <title>Una Mejor Forma de Consumir Contenido</title>
        <published>2024-04-11T00:00:00+00:00</published>
        <updated>2024-04-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Juan Manzanero
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://juanmanzanero.com/es/blog/una-mejor-forma-de-consumir-contenido/"/>
        <id>https://juanmanzanero.com/es/blog/una-mejor-forma-de-consumir-contenido/</id>
        
        <content type="html" xml:base="https://juanmanzanero.com/es/blog/una-mejor-forma-de-consumir-contenido/">&lt;h2 id=&quot;algoritmos-que-dictan-lo-que-ves&quot;&gt;Algoritmos que dictan lo que ves&lt;&#x2F;h2&gt;
&lt;p&gt;Las redes sociales no están diseñadas para mostrarte las noticias más novedosas e importantes,
sino para mostrarte contenido dictado por un algoritmo.&lt;&#x2F;p&gt;
&lt;p&gt;Y este contenido es normalmente, viral; y viral no quiere decir interesante.&lt;&#x2F;p&gt;
&lt;p&gt;Normalmente estos algoritmos priorizan contenido que te hacen enojar.&lt;&#x2F;p&gt;
&lt;p&gt;Contenido que promueve la negatividad obtiene más “clicks” que aquel que promueve la positividad.&lt;&#x2F;p&gt;
&lt;p&gt;Esa es la razón por la cual Twitter y Facebook están llenos de posts estúpidos e irrelevantes (por lo regular).&lt;&#x2F;p&gt;
&lt;p&gt;Por supuesto, es genial cuando el algoritmo te muestra contenido que te gusta,
descubriendo nuevas personas y páginas, pero eso no es lo usual.&lt;&#x2F;p&gt;
&lt;p&gt;Sin mencionar los molestos anuncios y más cosas que quieren que les hagas click.&lt;&#x2F;p&gt;
&lt;p&gt;Meta (anteriormente Facebook) sabe sobre esto, y promueve en sus productos como Instagram y Facebook,
lo mismo con Twitter.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;la-solucion-news-aggregators-rss&quot;&gt;La Solución: News Aggregators (RSS)&lt;&#x2F;h2&gt;
&lt;p&gt;RSS es un acrónimo de “Really Simple Syndication”.&lt;&#x2F;p&gt;
&lt;p&gt;Es una tecnología antigua, no muy promovida por las compañías.&lt;&#x2F;p&gt;
&lt;p&gt;Esto es debido a que cuando lees un post en un RSS Reader, no debes de visitar el website,
y el website no puede mostrarte anuncios usando Google Ads (por ejemplo). No generas tráfico,
tus visitas no cuentan, al menos no si no abres el link del post en tu RSS Reader.&lt;&#x2F;p&gt;
&lt;p&gt;Lo bueno que casi cualquier RSS Reader te muestra el contenido organizado por fechas,
no por un algoritmo raro que quiere hacerte enojar.&lt;&#x2F;p&gt;
&lt;p&gt;Para mi website utilizo un script en Node.js que toma todos los archivos &lt;code&gt;.mdx&lt;&#x2F;code&gt; dentro de &lt;code&gt;content&#x2F;blog&lt;&#x2F;code&gt;
y &lt;code&gt;content&#x2F;portfolio&lt;&#x2F;code&gt;, entonces genera los RSS Items, aquellos con &lt;code&gt;rss: true&lt;&#x2F;code&gt; en sus metadatos.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;como-usar-un-rss-reader&quot;&gt;Cómo usar un RSS Reader&lt;&#x2F;h2&gt;
&lt;p&gt;Primero, debes descargar uno.&lt;&#x2F;p&gt;
&lt;p&gt;Hay muchas opciones:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;netnewswire.com&#x2F;&quot;&gt;NetNewsWire&lt;&#x2F;a&gt;: un RSS Reader nativo para macOS y iOS, gratis y Open Source,
mi opción favorita como &lt;del&gt;Pecador&lt;&#x2F;del&gt; usuario de Apple&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;apps.kde.org&#x2F;akregator&quot;&gt;Akregator&lt;&#x2F;a&gt;: del proyecto KDE para Linux&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;play.google.com&#x2F;store&#x2F;apps&#x2F;details?id=com.nononsenseapps.feeder.play&quot;&gt;Feeder&lt;&#x2F;a&gt;: para Android&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ravenreader.app&#x2F;&quot;&gt;Raven Reader&lt;&#x2F;a&gt;: aplicación de escritorio multiplataforma&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;agregando-feeds&quot;&gt;Agregando Feeds&lt;&#x2F;h3&gt;
&lt;p&gt;Ahora debes buscar por el link RSS de tu website favorito, ¡&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;juanmanzanero.com&#x2F;es&#x2F;feed.xml&quot;&gt;como este&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;https:&#x2F;&#x2F;juanmanzanero.com&#x2F;es&#x2F;feed.xml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Si lo abres, verás una página rara con código similar a HTML.&lt;&#x2F;p&gt;
&lt;p&gt;Una vez copiado, ve a tu RSS app y busca “Agregar feed” o algo similar, y pega el link, ¡y listo!,
ahora obtendrás los últimos posts de mi website.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;agregando-feeds-de-redes-sociales&quot;&gt;Agregando Feeds de Redes Sociales&lt;&#x2F;h3&gt;
&lt;p&gt;Puedes agregar incluso feeds de sitios como Reddit o YouTube.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;reddit&quot;&gt;Reddit&lt;&#x2F;h4&gt;
&lt;p&gt;Solo cambia &lt;code&gt;[SUBREDDIT]&lt;&#x2F;code&gt; por el nombre del subreddit a añadir:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;https:&#x2F;&#x2F;reddit.com&#x2F;r&#x2F;[SUBREDDIT]&#x2F;new&#x2F;.rss&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h4 id=&quot;youtube&quot;&gt;YouTube&lt;&#x2F;h4&gt;
&lt;p&gt;Ve a el canal por añadir, luego ve a la pestaña “Acerca de”, da click en &lt;strong&gt;Compartir &amp;gt; Copiar ID del Canal&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Ahora solo cambia &lt;code&gt;[CHANNEL ID]&lt;&#x2F;code&gt; por el copiado:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;https:&#x2F;&#x2F;youtube.com&#x2F;feeds&#x2F;videos.xml?channel_id=[CHANNEL ID]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;mis-feeds-favoritos&quot;&gt;Mis Feeds Favoritos&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;juanmanzanero.com&#x2F;es&#x2F;feed.xml&quot;&gt;juanmanzanero.com (¡obviamente!)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;apod.com&#x2F;feed.rss&quot;&gt;Astronomic Picture of the Day (apod)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;feeds2.feedburner.com&#x2F;EarthSciencePictureoftheDay&quot;&gt;Earth Science Picture of the Day (epod)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ericmurphy.xyz&#x2F;index.xml&quot;&gt;Erick Murphy&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;lukesmith.xyz&#x2F;index.xml&quot;&gt;Luke Smith&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;mas-sobre-rss&quot;&gt;Más Sobre RSS&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.privacytools.io&#x2F;privacy-rss-feed-readers&quot;&gt;Privacy Tools - RSS Feed Readers&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.privacyguides.org&#x2F;en&#x2F;news-aggregators&#x2F;&quot;&gt;Privacy Guides - News Aggregators&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="es">
        <title>Construye una App Fullstack</title>
        <published>2024-01-18T00:00:00+00:00</published>
        <updated>2024-01-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Juan Manzanero
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://juanmanzanero.com/es/blog/construye-una-app-fullstack/"/>
        <id>https://juanmanzanero.com/es/blog/construye-una-app-fullstack/</id>
        
        <content type="html" xml:base="https://juanmanzanero.com/es/blog/construye-una-app-fullstack/">&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;juanmanzanero-com&#x2F;fullstack-app&quot;&gt;GitHub repo&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-introduccion&quot;&gt;1. Introducción&lt;&#x2F;h2&gt;
&lt;p&gt;En este tutorial, desarrollaremos una app fullstack con el siguiente tech stack:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;nextjs.org&#x2F;&quot;&gt;Next.js&lt;&#x2F;a&gt; como meta-framework&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tailwindcss.com&#x2F;&quot;&gt;TailwindCSS&lt;&#x2F;a&gt; para estilos&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ui.shadcn.com&#x2F;&quot;&gt;shadcn&#x2F;ui&lt;&#x2F;a&gt; para componentes UI&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.prisma.io&#x2F;&quot;&gt;Prisma&lt;&#x2F;a&gt; como ORM&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.postgresql.org&#x2F;&quot;&gt;PostgreSQL&lt;&#x2F;a&gt; como base de datos&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;authjs.dev&#x2F;&quot;&gt;Auth.js&lt;&#x2F;a&gt; para autenticación&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.docker.com&#x2F;&quot;&gt;Docker&lt;&#x2F;a&gt; para crear una instancia de una base de datos PostgreSQL localmente&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Aprenderemos algunos de los fundamentos de este tech stack, como usar &lt;strong&gt;server components&lt;&#x2F;strong&gt; en Next.js, o crear &lt;strong&gt;endpoints API&lt;&#x2F;strong&gt; usando el &lt;strong&gt;app router&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2-configuracion-inicial&quot;&gt;2. Configuración inicial&lt;&#x2F;h2&gt;
&lt;p&gt;Comencemos creando un nuevo proyecto Next.js, en tu &lt;code&gt;terminal&lt;&#x2F;code&gt; ejecuta:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;npx create-next-app@latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Asegúrate de marcar &lt;code&gt;Yes&lt;&#x2F;code&gt; en las siguientes opciones:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Would you like to use &lt;strong&gt;TypeScript&lt;&#x2F;strong&gt;?&lt;&#x2F;li&gt;
&lt;li&gt;Would you like to use &lt;strong&gt;ESLint&lt;&#x2F;strong&gt;?&lt;&#x2F;li&gt;
&lt;li&gt;Would you like to use &lt;strong&gt;Tailwind CSS&lt;&#x2F;strong&gt;?&lt;&#x2F;li&gt;
&lt;li&gt;Would you like to use &lt;strong&gt;‘src&#x2F;’ directory&lt;&#x2F;strong&gt;?&lt;&#x2F;li&gt;
&lt;li&gt;Would you like to use &lt;strong&gt;App Router&lt;&#x2F;strong&gt;? (recommended)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; What is your project named? fullstack-app&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; Would you like to use TypeScript? No &#x2F; Yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; Would you like to use ESLint? No &#x2F; Yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; Would you like to use Tailwind CSS? No &#x2F; Yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; Would you like to use `src&#x2F;` directory? No &#x2F; Yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; Would you like to use App Router? (recommended) No &#x2F; Yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; Would you like to customize the default import alias (@&#x2F;*)? No &#x2F; Yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; What import alias would you like configured? @&#x2F;*&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Espera a que la instalación de las dependencias termine, luego accede a la carpeta del proyecto:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cd fullstack-app&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Abre tu editor de código de preferencia.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-1-instalar-shadcn-ui&quot;&gt;2.1 Instalar shadcn&#x2F;ui&lt;&#x2F;h3&gt;
&lt;p&gt;Estos componentes nos ayudarán mucho a construir la &lt;strong&gt;UI&lt;&#x2F;strong&gt; junto con TailwindCSS.&lt;&#x2F;p&gt;
&lt;p&gt;Primero, inicializa shadcn&#x2F;ui:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;npx shadcn-ui@latest init&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Asegúrate de configurar shadcn&#x2F;ui de acuerdo a tu proyecto.&lt;&#x2F;p&gt;
&lt;p&gt;Puedes revisar la &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ui.shadcn.com&#x2F;docs&quot;&gt;documentación de shadcn&#x2F;ui&lt;&#x2F;a&gt; para cada componente que puedas necesitar, cada componente se instala individualmente.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-2-crear-una-base-de-datos-postgresql-usando-docker&quot;&gt;2.2 Crear una base de datos PostgreSQL usando Docker&lt;&#x2F;h3&gt;
&lt;p&gt;Asegúrate de tener &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.docker.com&#x2F;&quot;&gt;Docker&lt;&#x2F;a&gt; instalado en tu máquina.&lt;&#x2F;p&gt;
&lt;p&gt;Primero, necesitas descargar una imagen de PostgreSQL de Docker Hub:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;docker pull postgres&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Luego, crea un contenedor con la imagen:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;docker run --name my-postgres -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;2-3-instalar-prisma&quot;&gt;2.3 Instalar Prisma&lt;&#x2F;h3&gt;
&lt;p&gt;Instala Prisma usando tu gestor de dependencias, en este caso &lt;strong&gt;npm&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;npm install prisma -D&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Ahora inicializa Prisma:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;npx prisma init&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Se creará un directorio &lt;code&gt;.&#x2F;prisma&lt;&#x2F;code&gt; en la raíz de tu proyecto, con un archivo &lt;code&gt;schema.prisma&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Aquí crearás tus schemas.&lt;&#x2F;p&gt;
&lt;p&gt;Agrega este modelo como ejemplo:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-prisma-title=&quot;&quot;prisma&#x2F;schema.prisma&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model User {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  id        Int      @id @default(autoincrement())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  createdAt DateTime @default(now())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  email     String   @unique&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  name      String?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Actualiza tu archivo .env con la siguiente URL:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-env-title=&quot;&quot;.env&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;DATABASE_URL=&amp;quot;postgresql:&#x2F;&#x2F;postgres:password@localhost:5432&#x2F;postgres?schema=public&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;En esta URL está tu nombre de usuario (por defecto es postgres), tu contraseña (en este caso password), el host (por defecto es localhost), el puerto (por defecto es 5432), el nombre de la base de datos (por defecto es postgres) y el schema (por defecto es public).&lt;&#x2F;p&gt;
&lt;p&gt;Crea tu primera migración para probar si Prisma puede conectarse a tu base de datos local:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;npx prisma migrate dev --name init&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Si todo está bien, verás un nuevo directorio &lt;code&gt;&#x2F;migrations&lt;&#x2F;code&gt; con un nuevo archivo dentro.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Si tienes un error, asegúrate de poder conectarte a tu base de datos local. Elimina y crea el contenedor de nuevo si es necesario.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;2-4-configurar-auth-js&quot;&gt;2.4 Configurar Auth.js&lt;&#x2F;h3&gt;
&lt;p&gt;Agrega este modelo a tu &lt;code&gt;schema.prisma&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-prisma-title=&quot;&quot;prisma&#x2F;schema.prisma&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model Account {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  id                String  @id @default(cuid())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  userId            String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  type              String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  provider          String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  providerAccountId String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  refresh_token     String? @db.Text&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  access_token      String? @db.Text&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  expires_at        Int?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  token_type        String?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  scope             String?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  id_token          String? @db.Text&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  session_state     String?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  user User @relation(fields: [userId], references: [id], onDelete: Cascade)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  @@unique([provider, providerAccountId])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model Session {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  id           String   @id @default(cuid())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  sessionToken String   @unique&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  userId       String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  expires      DateTime&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model User {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  id            String    @id @default(cuid())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  name          String?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  email         String?   @unique&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  emailVerified DateTime?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  image         String?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  accounts      Account[]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  sessions      Session[]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model VerificationToken {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  identifier String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  token      String   @unique&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  expires    DateTime&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  @@unique([identifier, token])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Estos modelos son para &lt;strong&gt;Auth.js&lt;&#x2F;strong&gt;, ahora podemos instalarlo con el adaptador de Prisma:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;npm install @prisma&#x2F;client @auth&#x2F;prisma-adapter&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Instalaremos nodemailer también, ya que usaremos magic links para la autenticación:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;npm install nodemailer -D&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Ahora crea &lt;code&gt;src&#x2F;utils&#x2F;db.ts&lt;&#x2F;code&gt; e inicializa &lt;strong&gt;prisma&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-ts-title=&quot;&quot;src&#x2F;utils&#x2F;db.ts&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { PrismaClient } from &amp;quot;@prisma&#x2F;client&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;const prismaClientSingleton = () =&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  return new PrismaClient();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;declare global {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  var prisma: undefined | ReturnType&amp;lt;typeof prismaClientSingleton&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;const prisma = globalThis.prisma ?? prismaClientSingleton();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export default prisma;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;if (process.env.NODE_ENV !== &amp;quot;production&amp;quot;) globalThis.prisma = prisma;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Luego, crea &lt;code&gt;src&#x2F;libs&#x2F;auth.ts&lt;&#x2F;code&gt; para configurar Auth.js:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-ts-title=&quot;&quot;src&#x2F;utils&#x2F;auth.ts&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import type { NextAuthOptions } from &amp;quot;next-auth&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { PrismaAdapter } from &amp;quot;@auth&#x2F;prisma-adapter&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import EmailProvider from &amp;quot;next-auth&#x2F;providers&#x2F;email&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import prisma from &amp;quot;@&#x2F;libs&#x2F;db&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { Adapter } from &amp;quot;next-auth&#x2F;adapters&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export const authOptions = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  adapter: PrismaAdapter(prisma) as Adapter,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  providers: [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    EmailProvider({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      server: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        host: process.env.EMAIL_SERVER_HOST,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        port: process.env.EMAIL_SERVER_PORT,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        auth: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          user: process.env.EMAIL_SERVER_USER,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          pass: process.env.EMAIL_SERVER_PASSWORD,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      from: process.env.EMAIL_FROM,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  callbacks: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    session: async ({ session, user }) =&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      return {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ...session,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        user: user,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;} satisfies NextAuthOptions;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Esta configuración es para usar un proveedor de email, para este proyecto usaremos &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;resend.com&#x2F;&quot;&gt;Resend&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Crea una cuenta y luego obtén las siguientes credenciales en tu archivo .env:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;EMAIL_SERVER_HOST: smtp.resend.com&lt;&#x2F;li&gt;
&lt;li&gt;EMAIL_SERVER_PORT: 465&lt;&#x2F;li&gt;
&lt;li&gt;EMAIL_SERVER_USER: resend&lt;&#x2F;li&gt;
&lt;li&gt;EMAIL_FROM: onboarding@resend(dot)dev&lt;&#x2F;li&gt;
&lt;li&gt;EMAIL_SERVER_PASSWORD: yor api key&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Ahora, crea un archivo &lt;code&gt;src&#x2F;pages&#x2F;api&#x2F;auth&#x2F;[...nextauth].ts&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-ts-title=&quot;&quot;src&#x2F;app&#x2F;api&#x2F;auth&#x2F;[...nextauth]&#x2F;route.ts&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { authOptions } from &amp;quot;@&#x2F;libs&#x2F;auth&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import NextAuth from &amp;quot;next-auth&#x2F;next&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;const handler = NextAuth(authOptions);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export { handler as GET, handler as POST };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Este archivo es para manejar la autenticación en nuestra app.&lt;&#x2F;p&gt;
&lt;p&gt;Ahora puedes autenticar usuarios con un magic link enviado por email.&lt;&#x2F;p&gt;
&lt;p&gt;Crea un archivo &lt;code&gt;src&#x2F;app&#x2F;auth&#x2F;signin-form.tsx&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-tsx-title=&quot;&quot;src&#x2F;app&#x2F;auth&#x2F;signin-form.tsx&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;quot;use client&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { useState } from &amp;quot;react&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { signIn } from &amp;quot;next-auth&#x2F;react&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export default function SigninForm() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  const [email, setEmail] = useState&amp;lt;null | string&amp;gt;(null);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  async function handleSubmit() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    await signIn(&amp;quot;email&amp;quot;, {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      email,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      callbackUrl: `${window.location.origin}`,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  return (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;form className=&amp;quot;mt-5 space-y-4&amp;quot; action={handleSubmit}&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;section className=&amp;quot;flex flex-col gap-2&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;label htmlFor=&amp;quot;email&amp;quot;&amp;gt;Email&amp;lt;&#x2F;label&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;input&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          id=&amp;quot;email&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          type=&amp;quot;email&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          name=&amp;quot;email&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          onChange={(e) =&amp;gt; setEmail(e.target.value)}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          className=&amp;quot;w-max p-1 border border-slate-400&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&#x2F;section&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;button type=&amp;quot;submit&amp;quot;&amp;gt;Sign in&amp;lt;&#x2F;button&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&#x2F;form&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  );&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Impórtalo en &lt;code&gt;src&#x2F;app&#x2F;auth&#x2F;page.tsx&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;Import it to your &lt;code&gt;src&#x2F;app&#x2F;auth&#x2F;page.tsx&lt;&#x2F;code&gt; file:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-tsx-title=&quot;&quot;src&#x2F;app&#x2F;auth&#x2F;page.tsx&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { authOptions } from &amp;quot;@&#x2F;libs&#x2F;auth&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { getServerSession } from &amp;quot;next-auth&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { redirect } from &amp;quot;next&#x2F;navigation&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import SigninForm from &amp;quot;.&#x2F;form&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export default async function Signin() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  const session = await getServerSession(authOptions);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  if (session) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    return redirect(&amp;quot;&#x2F;&amp;quot;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  return (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;h1&amp;gt;Sign in&amp;lt;&#x2F;h1&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;SigninForm &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  );&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Como puedes ver, puedes redirigir a los usuarios si no están autenticados obteniendo la sesión con &lt;strong&gt;getServerSession&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;3-mejora-tu-ui&quot;&gt;3. Mejora tu UI&lt;&#x2F;h2&gt;
&lt;p&gt;Creemos una app para posts cortos.&lt;&#x2F;p&gt;
&lt;p&gt;Primero, agrega algunos componentes de shadcn&#x2F;ui y actualiza tus componentes, también crearemos nuevos componentes:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;npx shadcn-ui@latest add button&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;npx shadcn-ui@latest add dialog&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;npx shadcn-ui@latest add input&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;npx shadcn-ui@latest add textarea&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;npx shadcn-ui@latest add form&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;npx shadcn-ui@latest add label&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;npx shadcn-ui@latest add sonner&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Agregaremos las URL de los endpoints para estos componentes, pero más adelante.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;src&#x2F;app&#x2F;auth&#x2F;signin-form.tsx&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Aquí actualizaremos la UI y agregaremos validación de formulario.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-tsx-title=&quot;&quot;src&#x2F;app&#x2F;auth&#x2F;signin-form.tsx&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;quot;use client&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { signIn } from &amp;quot;next-auth&#x2F;react&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { Button } from &amp;quot;@&#x2F;components&#x2F;ui&#x2F;button&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Form,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  FormControl,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  FormField,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  FormItem,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  FormLabel,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  FormMessage,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;} from &amp;quot;@&#x2F;components&#x2F;ui&#x2F;form&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { Input } from &amp;quot;@&#x2F;components&#x2F;ui&#x2F;input&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { Textarea } from &amp;quot;@&#x2F;components&#x2F;ui&#x2F;textarea&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { useForm } from &amp;quot;react-hook-form&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import * as z from &amp;quot;zod&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { zodResolver } from &amp;quot;@hookform&#x2F;resolvers&#x2F;zod&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;const formSchema = z.object({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  email: z.string().email(),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;});&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export default function SigninForm() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  const form = useForm&amp;lt;z.infer&amp;lt;typeof formSchema&amp;gt;&amp;gt;({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    resolver: zodResolver(formSchema),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    defaultValues: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      email: &amp;quot;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  async function onSubmit({ email }: z.infer&amp;lt;typeof formSchema&amp;gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    await signIn(&amp;quot;email&amp;quot;, {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      email,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      callbackUrl: `${window.location.origin}`,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  return (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;Form {...form}&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;form className=&amp;quot;space-y-4&amp;quot; onSubmit={form.handleSubmit(onSubmit)}&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;FormField&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          control={form.control}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          name=&amp;quot;email&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          render={({ field }) =&amp;gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;FormItem&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              &amp;lt;FormLabel&amp;gt;Email&amp;lt;&#x2F;FormLabel&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              &amp;lt;FormControl&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                &amp;lt;Input placeholder=&amp;quot;address@example.com&amp;quot; {...field} &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              &amp;lt;&#x2F;FormControl&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              &amp;lt;FormMessage &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;&#x2F;FormItem&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          )}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;Button className=&amp;quot;w-full&amp;quot; type=&amp;quot;submit&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          Send magic link&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&#x2F;Button&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&#x2F;form&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&#x2F;Form&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  );&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;src&#x2F;components&#x2F;post&#x2F;create.tsx&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Actualiza los componentes de UI y agrega validación de formulario.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-tsx-title=&quot;&quot;src&#x2F;components&#x2F;post&#x2F;create.tsx&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;quot;use client&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { useState } from &amp;quot;react&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Dialog,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  DialogContent,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  DialogDescription,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  DialogHeader,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  DialogTitle,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  DialogTrigger,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;} from &amp;quot;@&#x2F;components&#x2F;ui&#x2F;dialog&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { Button } from &amp;quot;@&#x2F;components&#x2F;ui&#x2F;button&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Form,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  FormControl,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  FormField,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  FormItem,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  FormLabel,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  FormMessage,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;} from &amp;quot;@&#x2F;components&#x2F;ui&#x2F;form&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { Input } from &amp;quot;@&#x2F;components&#x2F;ui&#x2F;input&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { Textarea } from &amp;quot;@&#x2F;components&#x2F;ui&#x2F;textarea&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { useForm } from &amp;quot;react-hook-form&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import * as z from &amp;quot;zod&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { zodResolver } from &amp;quot;@hookform&#x2F;resolvers&#x2F;zod&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { useRouter } from &amp;quot;next&#x2F;navigation&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { toast } from &amp;quot;sonner&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { SessionProps } from &amp;quot;.&#x2F;types&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;const formSchema = z.object({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  title: z.string().min(1).max(100),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  content: z.string().min(1),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;});&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export default function CreatePost(props: SessionProps) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  const [open, setOpen] = useState(false);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  const router = useRouter();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  const form = useForm&amp;lt;z.infer&amp;lt;typeof formSchema&amp;gt;&amp;gt;({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    resolver: zodResolver(formSchema),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    defaultValues: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      title: &amp;quot;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      content: &amp;quot;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  async function onSubmit(values: z.infer&amp;lt;typeof formSchema&amp;gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    try {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      const res = await fetch(&amp;quot;&#x2F;api&#x2F;posts&amp;quot;, {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        method: &amp;quot;POST&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        headers: { &amp;quot;Content-Type&amp;quot;: &amp;quot;application&#x2F;json&amp;quot; },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        body: JSON.stringify({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          ...values,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          authorId: props.session.user?.id,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      const json = await res.json();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      if (!res.ok) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        toast(json.message);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        return;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      toast(&amp;quot;Post created!&amp;quot;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      form.reset();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      setOpen(false);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      router.refresh();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    } catch (error) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      console.error(error);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  return (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;Dialog open={open} onOpenChange={setOpen}&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;DialogTrigger asChild&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;Button&amp;gt;Create post&amp;lt;&#x2F;Button&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&#x2F;DialogTrigger&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;DialogContent className=&amp;quot;max-w-[300px]&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;DialogHeader className=&amp;quot;text-left&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;DialogTitle&amp;gt;Create post&amp;lt;&#x2F;DialogTitle&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;DialogDescription&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            Please &amp;lt;strong&amp;gt;do not&amp;lt;&#x2F;strong&amp;gt; post &amp;lt;strong&amp;gt;NSFW&amp;lt;&#x2F;strong&amp;gt; content.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;&#x2F;DialogDescription&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&#x2F;DialogHeader&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;Form {...form}&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;form className=&amp;quot;space-y-4&amp;quot; onSubmit={form.handleSubmit(onSubmit)}&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;FormField&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              control={form.control}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              name=&amp;quot;title&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              render={({ field }) =&amp;gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                &amp;lt;FormItem&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  &amp;lt;FormLabel&amp;gt;Title&amp;lt;&#x2F;FormLabel&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  &amp;lt;FormControl&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    &amp;lt;Input placeholder=&amp;quot;Hi there!&amp;quot; {...field} &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  &amp;lt;&#x2F;FormControl&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  &amp;lt;FormMessage &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                &amp;lt;&#x2F;FormItem&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              )}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;FormField&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              control={form.control}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              name=&amp;quot;content&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              render={({ field }) =&amp;gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                &amp;lt;FormItem&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  &amp;lt;FormLabel&amp;gt;Content&amp;lt;&#x2F;FormLabel&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  &amp;lt;FormControl&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    &amp;lt;Textarea&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                      placeholder=&amp;quot;Testing this great app!&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                      {...field}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  &amp;lt;&#x2F;FormControl&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  &amp;lt;FormMessage &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                &amp;lt;&#x2F;FormItem&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              )}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;Button className=&amp;quot;w-full&amp;quot; type=&amp;quot;submit&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              Post&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;&#x2F;Button&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;&#x2F;form&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&#x2F;Form&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&#x2F;DialogContent&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&#x2F;Dialog&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  );&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;src&#x2F;app&#x2F;page.tsx&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Haz fetch de los dats con Prisma, como esta página es un server component, podemos hacer fetch directamente.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-tsx-title=&quot;&quot;src&#x2F;app&#x2F;page.tsx&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { authOptions } from &amp;quot;@&#x2F;libs&#x2F;auth&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { getServerSession } from &amp;quot;next-auth&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import prisma from &amp;quot;@&#x2F;libs&#x2F;db&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import CreatePost from &amp;quot;@&#x2F;components&#x2F;post&#x2F;create&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import Post from &amp;quot;@&#x2F;components&#x2F;post&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export default async function Home() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  const session = await getServerSession(authOptions);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &#x2F;&#x2F; You can fetch data to Prisma in server components&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  const posts = await prisma.post.findMany({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    include: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      author: true,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  return (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;h1 className=&amp;quot;mb-5 font-bold text-xl&amp;quot;&amp;gt;Home&amp;lt;&#x2F;h1&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      {session ? (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;CreatePost session={session} &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ) : (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;p&amp;gt;You are not logged in&amp;lt;&#x2F;p&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      )}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;h3 className=&amp;quot;text-lg font-semibold mt-10&amp;quot;&amp;gt;Posts&amp;lt;&#x2F;h3&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;ul className=&amp;quot;mt-5 space-y-2.5&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        {posts.length &amp;gt; 0 ? (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          posts.map((post) =&amp;gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;li key={post.id}&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              &amp;lt;Post {...post} session={session} &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;&#x2F;li&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          ))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ) : (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;p&amp;gt;No posts&amp;lt;&#x2F;p&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&#x2F;ul&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  );&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;src&#x2F;components&#x2F;post&#x2F;item.tsx&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-tsx-title=&quot;&quot;src&#x2F;components&#x2F;post&#x2F;item.tsx&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;quot;use client&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import DeletePost from &amp;quot;.&#x2F;delete&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import EditPost from &amp;quot;.&#x2F;edit&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { TPostProps } from &amp;quot;.&#x2F;types&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export default function PostItem(props: TPostProps) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  return (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;article className=&amp;quot;w-max p-2 border border-slate-500 rounded-md&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;header className=&amp;quot;flex justify-between items-center&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;h2 className=&amp;quot;font-bold text-lg&amp;quot;&amp;gt;{props.title}&amp;lt;&#x2F;h2&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        {props.session?.user?.id === props.authorId &amp;amp;&amp;amp; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;section className=&amp;quot;space-x-2&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;EditPost {...props} &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;DeletePost {...props} &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;&#x2F;section&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&#x2F;header&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;p&amp;gt;{props.content}&amp;lt;&#x2F;p&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;span className=&amp;quot;text-sm&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        Posted by {props.author?.email || &amp;quot;anon&amp;quot;} at{&amp;quot; &amp;quot;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        {new Date(props.createdAt).toLocaleString()}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&#x2F;span&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&#x2F;article&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  );&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;src&#x2F;components&#x2F;post&#x2F;edit.tsx&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Crea un botón para abrir un diálogo que renderice los datos del post para editar, agrega validación y fetch al endpoint de la API.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-tsx-title=&quot;&quot;src&#x2F;components&#x2F;post&#x2F;edit.tsx&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;quot;use client&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { useState } from &amp;quot;react&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { Edit } from &amp;quot;lucide-react&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Dialog,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  DialogClose,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  DialogContent,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  DialogDescription,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  DialogHeader,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  DialogTitle,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  DialogTrigger,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;} from &amp;quot;@&#x2F;components&#x2F;ui&#x2F;dialog&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Form,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  FormControl,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  FormField,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  FormItem,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  FormLabel,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  FormMessage,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;} from &amp;quot;@&#x2F;components&#x2F;ui&#x2F;form&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { Input } from &amp;quot;@&#x2F;components&#x2F;ui&#x2F;input&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { Textarea } from &amp;quot;@&#x2F;components&#x2F;ui&#x2F;textarea&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { useForm } from &amp;quot;react-hook-form&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import * as z from &amp;quot;zod&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { zodResolver } from &amp;quot;@hookform&#x2F;resolvers&#x2F;zod&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { useRouter } from &amp;quot;next&#x2F;navigation&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { toast } from &amp;quot;sonner&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { Button } from &amp;quot;@&#x2F;components&#x2F;ui&#x2F;button&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { TPostProps } from &amp;quot;.&#x2F;types&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;const formSchema = z.object({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  title: z.string().min(1).max(100),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  content: z.string().min(1),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;});&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export default function EditPost(props: TPostProps) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  const [open, setOpen] = useState(false);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  const router = useRouter();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  const form = useForm&amp;lt;z.infer&amp;lt;typeof formSchema&amp;gt;&amp;gt;({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    resolver: zodResolver(formSchema),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    defaultValues: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      title: props.title,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      content: props.content,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  async function onSubmit(values: z.infer&amp;lt;typeof formSchema&amp;gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    try {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      const res = await fetch(&amp;quot;&#x2F;api&#x2F;posts&amp;quot;, {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        method: &amp;quot;PUT&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        headers: { &amp;quot;Content-Type&amp;quot;: &amp;quot;application&#x2F;json&amp;quot; },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        body: JSON.stringify({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          ...values,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          id: props.id,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      const json = await res.json();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      if (!res.ok) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        toast(json.message);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        return;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      toast(&amp;quot;Post edited!&amp;quot;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      form.reset();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      setOpen(false);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      router.refresh();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    } catch (error) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      console.error(error);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  return (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;Dialog open={open} onOpenChange={setOpen}&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;DialogTrigger asChild&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;Button variant=&amp;quot;secondary&amp;quot; size=&amp;quot;icon&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;Edit &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&#x2F;Button&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&#x2F;DialogTrigger&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;DialogContent className=&amp;quot;max-w-[300px]&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;DialogHeader className=&amp;quot;text-left&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;DialogTitle&amp;gt;Edit post&amp;lt;&#x2F;DialogTitle&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;DialogDescription&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            Please &amp;lt;strong&amp;gt;do not&amp;lt;&#x2F;strong&amp;gt; post &amp;lt;strong&amp;gt;NSFW&amp;lt;&#x2F;strong&amp;gt; content.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;&#x2F;DialogDescription&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&#x2F;DialogHeader&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;Form {...form}&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;form className=&amp;quot;space-y-4&amp;quot; onSubmit={form.handleSubmit(onSubmit)}&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;FormField&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              control={form.control}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              name=&amp;quot;title&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              render={({ field }) =&amp;gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                &amp;lt;FormItem&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  &amp;lt;FormLabel&amp;gt;Title&amp;lt;&#x2F;FormLabel&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  &amp;lt;FormControl&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    &amp;lt;Input placeholder=&amp;quot;Hi there!&amp;quot; {...field} &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  &amp;lt;&#x2F;FormControl&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  &amp;lt;FormMessage &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                &amp;lt;&#x2F;FormItem&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              )}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;FormField&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              control={form.control}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              name=&amp;quot;content&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              render={({ field }) =&amp;gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                &amp;lt;FormItem&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  &amp;lt;FormLabel&amp;gt;Content&amp;lt;&#x2F;FormLabel&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  &amp;lt;FormControl&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    &amp;lt;Textarea&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                      placeholder=&amp;quot;Testing this great app!&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                      {...field}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  &amp;lt;&#x2F;FormControl&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                  &amp;lt;FormMessage &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                &amp;lt;&#x2F;FormItem&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              )}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;DialogClose asChild&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              &amp;lt;Button className=&amp;quot;w-full&amp;quot; type=&amp;quot;submit&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                Edit post&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              &amp;lt;&#x2F;Button&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;&#x2F;DialogClose&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;&#x2F;form&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&#x2F;Form&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&#x2F;DialogContent&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&#x2F;Dialog&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  );&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;src&#x2F;components&#x2F;post&#x2F;delete.tsx&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Crea un botón para abrir un diálogo para eliminar el post, agrega validación y fetch al endpoint de la API.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-tsx-title=&quot;&quot;src&#x2F;components&#x2F;post&#x2F;delete.tsx&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;quot;use client&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { LucideTrash2 } from &amp;quot;lucide-react&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { Button } from &amp;quot;@&#x2F;components&#x2F;ui&#x2F;button&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Dialog,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  DialogClose,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  DialogContent,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  DialogDescription,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  DialogHeader,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  DialogTitle,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  DialogTrigger,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;} from &amp;quot;@&#x2F;components&#x2F;ui&#x2F;dialog&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { useRouter } from &amp;quot;next&#x2F;navigation&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { toast } from &amp;quot;sonner&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { TPostProps } from &amp;quot;.&#x2F;types&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export default function DeletePost(props: TPostProps) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  const router = useRouter();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  async function handleDelete() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    try {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      const res = await fetch(&amp;quot;&#x2F;api&#x2F;posts&amp;quot;, {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        method: &amp;quot;DELETE&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        headers: { &amp;quot;Content-Type&amp;quot;: &amp;quot;application&#x2F;json&amp;quot; },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        body: JSON.stringify({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          id: props.id,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      const json = await res.json();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      if (!res.ok) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        toast(json.message);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        return;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      toast(&amp;quot;Post deleted!&amp;quot;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      router.refresh();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    } catch (error) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      console.error(error);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  return (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;Dialog&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;DialogTrigger asChild&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;Button variant=&amp;quot;destructive&amp;quot; size=&amp;quot;icon&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;LucideTrash2 &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&#x2F;Button&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&#x2F;DialogTrigger&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;DialogContent className=&amp;quot;max-w-[300px]&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;DialogHeader className=&amp;quot;text-left&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;DialogTitle&amp;gt;Delete post&amp;lt;&#x2F;DialogTitle&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;DialogDescription&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            Are you sure you want to &amp;lt;strong&amp;gt;delete&amp;lt;&#x2F;strong&amp;gt; this post? This&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            action cannot be undone.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;&#x2F;DialogDescription&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&#x2F;DialogHeader&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;footer className=&amp;quot;flex flex-col gap-2&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;DialogClose asChild&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;Button variant=&amp;quot;secondary&amp;quot; className=&amp;quot;w-full&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              No, keep post&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;&#x2F;Button&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;&#x2F;DialogClose&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;DialogClose asChild&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;Button&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              onClick={handleDelete}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              variant=&amp;quot;destructive&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              className=&amp;quot;w-full&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              Yes, delete post&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;&#x2F;Button&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;&#x2F;DialogClose&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&#x2F;footer&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&#x2F;DialogContent&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&#x2F;Dialog&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  );&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;src&#x2F;components&#x2F;post&#x2F;types.ts&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-ts-title=&quot;&quot;src&#x2F;components&#x2F;post&#x2F;types.ts&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;type SessionProps = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  session: any;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;type TPostProps = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  author: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    id: string;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    name: string | null;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    email: string | null;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    emailVerified: Date | null;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    image: string | null;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  } | null;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  id: string;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  createdAt: Date;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  updatedAt: Date;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  title: string;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  content: string;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  authorId: string | null;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  session: any;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export type { SessionProps, TPostProps };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;src&#x2F;components&#x2F;sign-out.tsx&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Un botón simple para cerrar sesión.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-tsx-title=&quot;&quot;src&#x2F;components&#x2F;sign-out.tsx&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;quot;use client&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { signOut } from &amp;quot;next-auth&#x2F;react&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { Button } from &amp;quot;.&#x2F;ui&#x2F;button&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export default function SignOut() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  return &amp;lt;Button onClick={() =&amp;gt; signOut()}&amp;gt;Sign out&amp;lt;&#x2F;Button&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;src&#x2F;components&#x2F;navbar.tsx&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Renderiza el botón de cerrar sesión o iniciar sesión dependiendo si el usuario está autenticado o no.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-tsx-title=&quot;&quot;src&#x2F;components&#x2F;navbar.tsx&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import Link from &amp;quot;next&#x2F;link&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { Button } from &amp;quot;.&#x2F;ui&#x2F;button&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { getServerSession } from &amp;quot;next-auth&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { authOptions } from &amp;quot;@&#x2F;libs&#x2F;auth&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import SignOut from &amp;quot;.&#x2F;sign-out&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export default async function Navbar() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  const session = await getServerSession(authOptions);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  return (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;nav className=&amp;quot;w-full p-4 border-b flex justify-between items-center&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;section&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;Button variant=&amp;quot;link&amp;quot; className=&amp;quot;px-0 font-semibold text-lg&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;Link href=&amp;quot;&#x2F;&amp;quot;&amp;gt;Fullstack app&amp;lt;&#x2F;Link&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&#x2F;Button&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&#x2F;section&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;section&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        {session ? (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;SignOut &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ) : (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;Button asChild&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;Link href=&amp;quot;&#x2F;auth&amp;quot;&amp;gt;Sign in&amp;lt;&#x2F;Link&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;&#x2F;Button&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&#x2F;section&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&#x2F;nav&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  );&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;src&#x2F;app&#x2F;layout.tsx&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Agrega tu &lt;strong&gt;Navbar&lt;&#x2F;strong&gt; y &lt;strong&gt;Toaster&lt;&#x2F;strong&gt; y algunos estilos.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-tsx-title=&quot;&quot;src&#x2F;app&#x2F;layout.tsx&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { Inter } from &amp;quot;next&#x2F;font&#x2F;google&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import Navbar from &amp;quot;@&#x2F;components&#x2F;navbar&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { Toaster } from &amp;quot;@&#x2F;components&#x2F;ui&#x2F;sonner&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import &amp;quot;.&#x2F;globals.css&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;const inter = Inter({ subsets: [&amp;quot;latin&amp;quot;] });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;interface Props extends React.PropsWithChildren {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export default function RootLayout(props: Props) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  return (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;body className={inter.className}&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;Navbar &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;main className=&amp;quot;px-4 py-8&amp;quot;&amp;gt;{props.children}&amp;lt;&#x2F;main&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;Toaster &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&#x2F;body&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&#x2F;html&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  );&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;4-anade-funcionalidad-crud&quot;&gt;4. Añade funcionalidad CRUD&lt;&#x2F;h2&gt;
&lt;p&gt;Ahora podemos crear, leer, actualizar y eliminar posts.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;prisma&#x2F;schema.prisma&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Actualiza tu schema de Prisma con el modelo Post:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-prisma-title=&quot;&quot;prisma&#x2F;schema.prisma&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;generator client {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  provider = &amp;quot;prisma-client-js&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;datasource db {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  provider = &amp;quot;postgresql&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  url      = env(&amp;quot;DATABASE_URL&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model Account {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  id                String  @id @default(cuid())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  userId            String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  type              String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  provider          String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  providerAccountId String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  refresh_token     String? @db.Text&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  access_token      String? @db.Text&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  expires_at        Int?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  token_type        String?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  scope             String?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  id_token          String? @db.Text&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  session_state     String?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  user User @relation(fields: [userId], references: [id], onDelete: Cascade)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  @@unique([provider, providerAccountId])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model Session {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  id           String   @id @default(cuid())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  sessionToken String   @unique&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  userId       String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  expires      DateTime&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model User {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  id            String    @id @default(cuid())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  name          String?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  email         String?   @unique&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  emailVerified DateTime?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  image         String?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  accounts      Account[]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  sessions      Session[]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  posts         Post[]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model VerificationToken {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  identifier String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  token      String   @unique&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  expires    DateTime&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  @@unique([identifier, token])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model Post {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  id        String   @id @default(cuid())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  createdAt DateTime @default(now())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  updatedAt DateTime @updatedAt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  title     String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  content   String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  author    User     @relation(fields: [authorId], references: [id])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  authorId  String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Genera una nueva migración:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-bash-title=&quot;&quot;Terminal&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;npx prisma migrate dev --name add-posts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Ahora, create un archivo &lt;code&gt;src&#x2F;app&#x2F;api&#x2F;posts&#x2F;route.ts&lt;&#x2F;code&gt; con las funciones asíncronas &lt;strong&gt;GET&lt;&#x2F;strong&gt;, &lt;strong&gt;PUT&lt;&#x2F;strong&gt; y &lt;strong&gt;DELETE&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-ts-title=&quot;&quot;src&#x2F;app&#x2F;api&#x2F;posts&#x2F;route.ts&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import prisma from &amp;quot;@&#x2F;libs&#x2F;db&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { NextRequest, NextResponse } from &amp;quot;next&#x2F;server&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export async function POST(req: Request) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  try {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    if (!req.body) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      return NextResponse.json({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ok: false,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        status: 400,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        message: &amp;quot;Data required&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    const json = await req.json();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    const res = await prisma.post.create({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      data: json,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    return NextResponse.json({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ok: true,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      status: 201,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      data: res,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  } catch (error) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    if (error instanceof Error) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      return NextResponse.json({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ok: false,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        status: 500,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        message: error.message,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    return NextResponse.json({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ok: false,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      status: 500,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      message: &amp;quot;Internal server error&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export async function PUT(req: NextRequest) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  try {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    const body = await req.json();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    const res = await prisma.post.update({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      where: { id: body.id },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      data: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        title: body.title,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        content: body.content,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    return NextResponse.json({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ok: true,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      status: 200,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      data: res,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  } catch (error) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    if (error instanceof Error) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      return NextResponse.json({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ok: false,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        status: 500,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        message: error.message,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    return NextResponse.json({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ok: false,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      status: 500,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      message: &amp;quot;Internal server error&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export async function DELETE(req: NextRequest) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  try {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    const body = await req.json();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    const res = await prisma.post.delete({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      where: { id: body.id },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    return NextResponse.json({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ok: true,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      status: 200,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      data: res,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  } catch (error) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    if (error instanceof Error) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      return NextResponse.json({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ok: false,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        status: 500,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        message: error.message,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    return NextResponse.json({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ok: false,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      status: 500,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      message: &amp;quot;Internal server error&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Intenta crear un post en el home.&lt;&#x2F;p&gt;
&lt;p&gt;La página se refrescará y verás el post que creaste, solo tú puedes editarlo o eliminarlo.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;5-conclusion&quot;&gt;5. Conclusión&lt;&#x2F;h2&gt;
&lt;p&gt;Como puedes ver, crear una app fullstack con Next.js es muy fácil.&lt;&#x2F;p&gt;
&lt;p&gt;Por supuesto, se puede mejorar, agregando validación del lado del servidor para los inputs, agregando paginación para los posts en el home, etc.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="es">
        <title>Next Intl Blog Template</title>
        <published>2023-12-18T00:00:00+00:00</published>
        <updated>2023-12-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Juan Manzanero
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://juanmanzanero.com/es/blog/next-intl-blog-template/"/>
        <id>https://juanmanzanero.com/es/blog/next-intl-blog-template/</id>
        
        <content type="html" xml:base="https://juanmanzanero.com/es/blog/next-intl-blog-template/">&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;juanmanzanero-com&#x2F;next-intl-blog-template&quot;&gt;GitHub&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;next-intl-blog-template.vercel.app&#x2F;en&quot;&gt;Website&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;introduccion&quot;&gt;Introducción&lt;&#x2F;h2&gt;
&lt;p&gt;Recientemente actualicé este website, y como sabrás, es un website con &lt;strong&gt;contenido en Inglés y Español&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;No estoy usando un plugin de traducción, en su lugar escribo cada palabra en ambos idiomas.&lt;&#x2F;p&gt;
&lt;p&gt;Gracias a Next.js y &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;next-intl-docs.vercel.app&#x2F;&quot;&gt;next-intl&lt;&#x2F;a&gt; puedo lograr esto, renderizando rutas para cada idioma en el website, accediendo a un diccionario que contiene el contenido traducido por mí.&lt;&#x2F;p&gt;
&lt;p&gt;Para los archivos .mdx, creé un directorio para cada idioma, y dentro de esos directorios contiene el contenido en ambos idiomas también.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;como-usar&quot;&gt;Cómo usar&lt;&#x2F;h2&gt;
&lt;p&gt;Este template es una extensión de &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;next-intl-docs.vercel.app&#x2F;&quot;&gt;next-intl&lt;&#x2F;a&gt;, revisa la &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;next-intl-docs.vercel.app&#x2F;docs&#x2F;getting-started&quot;&gt;guía de inicio&lt;&#x2F;a&gt; para aprender lo básico, el propósito del template es crear un layout simple para futuras personalizaciones.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;agregar-o-quitar-locales&quot;&gt;Agregar o quitar locales&lt;&#x2F;h3&gt;
&lt;p&gt;Puedes agregar o remover locales en el archivo &lt;code&gt;src&#x2F;lang&#x2F;locales.ts&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-ts-title=&quot;&quot;src&#x2F;lang&#x2F;locales.ts&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export type locales = &amp;#39;en&amp;#39; | &amp;#39;es&amp;#39;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export const localesList: locales[] = [&amp;#39;en&amp;#39;, &amp;#39;es&amp;#39;];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Solo agrega o remueve un locale de la constante &lt;code&gt;locales&lt;&#x2F;code&gt;, y agrega o remueve el locale de la lista.&lt;&#x2F;p&gt;
&lt;p&gt;El primer item en &lt;code&gt;localesList&lt;&#x2F;code&gt; debe ser el locale por default.&lt;&#x2F;p&gt;
&lt;p&gt;La lista es usada para la generación de rutas estáticas en
&lt;code&gt;src&#x2F;app&#x2F;[locale]&#x2F;layout.tsx&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-ts-title=&quot;&quot;src&#x2F;app&#x2F;[locale]&#x2F;layout.tsx&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { localesList } from &amp;#39;@&#x2F;lang&#x2F;locales&amp;#39;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export function generateStaticParams() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  return localesList.map((locale) =&amp;gt; ({ locale }));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Recuerda actualizar el matcher en &lt;code&gt;src&#x2F;middleware.ts&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-ts-title=&quot;&quot;src&#x2F;middleware.ts&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&#x2F;&#x2F;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export const config = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  matcher: [&amp;#39;&#x2F;&amp;#39;, &amp;#39;&#x2F;(en|es)&#x2F;:path*&amp;#39;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Y por supuesto, actualiza los archivos &lt;code&gt;src&#x2F;lang&#x2F;[locale].json&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;crear-contenido&quot;&gt;Crear contenido&lt;&#x2F;h3&gt;
&lt;p&gt;Usa &lt;code&gt;src&#x2F;content&#x2F;[locale]&lt;&#x2F;code&gt; para crear contenido, en el directorio &lt;code&gt;&#x2F;[locale]&#x2F;&lt;&#x2F;code&gt; crea un directorio para cada propósito, por ejemplo: &lt;code&gt;&#x2F;[locale]&#x2F;blog&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Dentro crea el archivo .mdx con un nombre único, el nombre será usado como slug para crear la página estática para ese post.&lt;&#x2F;p&gt;
&lt;p&gt;Para crear una sección de blog, usarás la función &lt;em&gt;getAllContent&lt;&#x2F;em&gt; en tu ruta, por ejemplo: &lt;code&gt;src&#x2F;app&#x2F;[locale]&#x2F;blog&#x2F;[slug]&#x2F;page.tsx&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-tsx-title=&quot;&quot;src&#x2F;app&#x2F;[locale]&#x2F;blog&#x2F;[slug]&#x2F;page.tsx&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { Mdx } from &amp;#39;@&#x2F;components&amp;#39;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { TParamsLocale, TPage, TSlugLang } from &amp;#39;@&#x2F;types&amp;#39;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { Metadata } from &amp;#39;next&amp;#39;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import { getAllContent, getContent } from &amp;#39;@&#x2F;utils&#x2F;getContent&amp;#39;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export async function generateStaticParams(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  props: TParamsLocale&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;): Promise&amp;lt;TSlugLang[]&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  const blogs = await getAllContent(props.params.locale, &amp;#39;blog&amp;#39;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  if (!blogs) return [];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  return blogs.map((blog) =&amp;gt; ({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    slug: blog.slug,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    locale: props.params.locale,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&#x2F;&#x2F;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Esto creará una página estática para cada post de blog.&lt;&#x2F;p&gt;
&lt;p&gt;Puedes obtener la metadata del archivo &lt;code&gt;.mdx&lt;&#x2F;code&gt; también.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-tsx-title=&quot;&quot;src&#x2F;app&#x2F;[locale]&#x2F;blog&#x2F;[slug]&#x2F;page.tsx&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&#x2F;&#x2F;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export async function generateMetadata(props: TPage): Promise&amp;lt;Metadata&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  const blog = await getContent(props.params.locale, &amp;#39;blog&amp;#39;, props.params.slug);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  if (!blog) return {};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  return {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    title: blog.title,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&#x2F;&#x2F;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Entonces, renderiza el contenido usando el componente &lt;em&gt;Mdx&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot; data-tsx-title=&quot;&quot;src&#x2F;app&#x2F;[locale]&#x2F;blog&#x2F;[slug]&#x2F;page.tsx&quot;&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&#x2F;&#x2F;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export default async function Page(props: TPage) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  const post = await getContent(props.params.locale, &amp;#39;blog&amp;#39;, props.params.slug);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  if (!post) return null;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  return &amp;lt;Mdx code={post.body.code} &#x2F;&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;juanmanzanero-com&#x2F;next-intl-blog-template&quot;&gt;Puedes hacer un fork de este template aquí&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="es">
        <title>Rewind 2023 y Planes Futuros</title>
        <published>2023-12-16T00:00:00+00:00</published>
        <updated>2023-12-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Juan Manzanero
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://juanmanzanero.com/es/blog/rewind-2023-y-planes-futuros/"/>
        <id>https://juanmanzanero.com/es/blog/rewind-2023-y-planes-futuros/</id>
        
        <content type="html" xml:base="https://juanmanzanero.com/es/blog/rewind-2023-y-planes-futuros/">&lt;p&gt;Mi rewind del 2023 y mis planes para el 2024 y más allá.&lt;&#x2F;p&gt;
&lt;p&gt;Espero que la estés pasando bien en estas fechas.&lt;&#x2F;p&gt;
&lt;p&gt;La vida es una sucesión de decisiones, y en retrospectiva, estoy feliz de que este año haya hecho
las correctas.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;en-retrospectiva-sobre-mi-carrera-este-2023&quot;&gt;En retrospectiva sobre mi carrera este 2023&lt;&#x2F;h2&gt;
&lt;p&gt;Crecí drásticamente como Full Stack Developer, aprendiendo nuevas libraries y estableciendo mi tech
stack.&lt;&#x2F;p&gt;
&lt;p&gt;Incluso comencé con hacking (no lo que sueles escuchar en las noticias), creando side projects
buscando crear una solución.&lt;&#x2F;p&gt;
&lt;p&gt;Actualicé este sitio, creando nuevas funcionalidades para crear contenido.&lt;&#x2F;p&gt;
&lt;p&gt;Ahora tengo más confianza en mis habilidades, listo para seguir creciendo y tomando nuevos desafios.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;planes-futuros-para-mi-carrera-este-2024&quot;&gt;Planes futuros para mi carrera este 2024&lt;&#x2F;h2&gt;
&lt;p&gt;Quiero comenzar a hacer freelancing, estaré creando templates y proyectos demo para vender mis
servicios como Frontend Developer principalmente, pero seguiré aprendiendo sobre Backend y Cloud,
así como seguiré practicando mi Inglés para entrar al mercado estadounidense o europeo.&lt;&#x2F;p&gt;
&lt;p&gt;Quizás no lo consiga este 2024, pero debo seguir creciendo, ya que cada año que pasa, estaré más
preparado.&lt;&#x2F;p&gt;
&lt;p&gt;Veré si podría contribuir a algún proyecto Open Source, ya que casí todas las herramientas que uso
son Open Source, y no podría estar aquí sino fuera por la ayuda de proyectos Open Source.&lt;&#x2F;p&gt;
&lt;p&gt;Honestamente, mi verdadero deseo es trabajar tiempo completo en una startup Software as a Service,
o cualquier startup que se enfoque en Software.&lt;&#x2F;p&gt;
&lt;p&gt;Lo bueno es que continuo creciendo profesionalmente, y espero (y lo haré) que el siguiente año
alcance más metas.&lt;&#x2F;p&gt;
&lt;p&gt;Y por supuesto, continuaré con el hacking (de forma creativa, no robando información u otras cosas criminales)
con side projects, crear una fuente extra de ingresos sería genial para mis finanzas.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;retrospectiva-personal-del-2023-y-mas&quot;&gt;Retrospectiva personal del 2023 y más&lt;&#x2F;h2&gt;
&lt;p&gt;Este año redescubrí el hobby de leer, y realmente lo disfruto.&lt;&#x2F;p&gt;
&lt;p&gt;Descubrí mi nuevo libro favorito, &lt;strong&gt;Ready Player One&lt;&#x2F;strong&gt;, fue una lectura emocionante, el siguiente año
leeré la secuela.&lt;&#x2F;p&gt;
&lt;p&gt;También comencé a leer &lt;strong&gt;Ikigai&lt;&#x2F;strong&gt;, para seguir adquiriendo buenos hábitos para una vida larga y feliz,
así como buscar un propósito en la vida.&lt;&#x2F;p&gt;
&lt;p&gt;Otro libro que comencé a leer es &lt;strong&gt;The Little Book of Common Sense Investing&lt;&#x2F;strong&gt;, ya que teniendo el buen
hábito de ahorrar, pero quiero que mi dinero siga creciendo más para un retiro digno.&lt;&#x2F;p&gt;
&lt;p&gt;Estoy gastando menos tiempo en redes sociales,
&lt;a href=&quot;&#x2F;es&#x2F;blog&#x2F;la-monotonia-de-las-redes-sociales&quot;&gt;incluso escribí un artículo sobre esto&lt;&#x2F;a&gt;,
haciendo cosas que &lt;strong&gt;realmente quiero hacer&lt;&#x2F;strong&gt; en su lugar.&lt;&#x2F;p&gt;
&lt;p&gt;Es realmente horrible cuanto tiempo nos roban las redes sociales, manteniéndonos alejados de hacer cosas
que realmente disfrutamos.&lt;&#x2F;p&gt;
&lt;p&gt;Estoy haciendo ejercicio moderado, pero dentro de casa, quiero ir afuera también, necesito más luz solar.&lt;&#x2F;p&gt;
&lt;p&gt;Estoy feliz de pasar tiempo con mi familia, incluso si la mayoría del tiempo estoy trabajando o estudiando,
mantengo contacto con mis seres queridos, y continuaré haciéndolo, desde luego.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;planes-personales-futuros-para-2024-y-mas&quot;&gt;Planes personales futuros para 2024 y más&lt;&#x2F;h2&gt;
&lt;p&gt;Seguiré leyendo, escribiré más para este website.&lt;&#x2F;p&gt;
&lt;p&gt;Quiero adquirir nuevos hábitos, como pixel art.&lt;&#x2F;p&gt;
&lt;p&gt;Retomaré un viejo hobby, &lt;strong&gt;GameDev&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Como Unity ha estado haciendo cosas cuestionables, usaré &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;godotengine.org&#x2F;&quot;&gt;Godot&lt;&#x2F;a&gt; en su lugar.&lt;&#x2F;p&gt;
&lt;p&gt;No tengo en mente algún proyecto grande o algo así, solo retomar el desarrollar demos sencillas de videojuegos,
estaría genial lanzar un videojuego pequeño, pero terminado.&lt;&#x2F;p&gt;
&lt;p&gt;Créeme, es muy, &lt;strong&gt;MUY&lt;&#x2F;strong&gt; difícil desarrollar videojuegos, así que en lugar de abrumarme, mantendré mis
&lt;strong&gt;ambiciones simples&lt;&#x2F;strong&gt;, pero &lt;strong&gt;constantes&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;felices-fiestas&quot;&gt;¡Felices fiestas!&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;juanmanzanero.com&#x2F;es&#x2F;blog&#x2F;rewind-2023-y-planes-futuros&#x2F;2023-complete!.png&quot; alt=&quot;2023 complete image!&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="es">
        <title>Participé en una Hackaton</title>
        <published>2023-08-16T00:00:00+00:00</published>
        <updated>2023-08-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Juan Manzanero
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://juanmanzanero.com/es/blog/participe-en-una-hackaton/"/>
        <id>https://juanmanzanero.com/es/blog/participe-en-una-hackaton/</id>
        
        <content type="html" xml:base="https://juanmanzanero.com/es/blog/participe-en-una-hackaton/">&lt;p&gt;Recientemente participé en un &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;supabase.com&#x2F;blog&#x2F;supabase-lw8-hackathon&quot;&gt;Hackathon de Supabase&lt;&#x2F;a&gt;,
formando un equipo con personas de otros países, uno de Bangladesh y otro de Brasil,
utilizando inglés para comunicarnos en Discord.&lt;&#x2F;p&gt;
&lt;p&gt;La temática del Hackathon era libre, la única regla principal era utilizar Supabase para cualquier función,
como autenticación, como base de datos PostgreSQL o para utilizar vectores en la inteligencia artificial.&lt;&#x2F;p&gt;
&lt;p&gt;Tuvimos 10 días para construir un producto utilizando cualquier tecnología y subirlo a un repositorio en GitHub.&lt;&#x2F;p&gt;
&lt;p&gt;Desarrollamos una aplicación de comercio electrónico con un modelo de productos similar a Walmart,
implementando vectores para obtener mejores resultados en las búsquedas.&lt;&#x2F;p&gt;
&lt;p&gt;Utilizando Supabase, implementamos autenticación y rutas protegidas,
de manera que el usuario deba iniciar sesión para ver recomendaciones y más.&lt;&#x2F;p&gt;
&lt;p&gt;El usuario puede agregar productos al carrito de compras y revisar sus artículos para guardarlos y
ver recomendaciones, así como los artículos que se compran con frecuencia.&lt;&#x2F;p&gt;
&lt;p&gt;Mi papel principal se centró en crear la interfaz de usuario utilizando Next.js 13app router,
protegiendo las rutas solo para usuarios autenticados, y creando componentes reutilizables como
tarjetas de producto, y por supuesto, haciendo que el diseño sea adaptable tanto para dispositivos móviles
como para escritorio.&lt;&#x2F;p&gt;
&lt;p&gt;Utilizamos &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ui.shadcn.com&#x2F;&quot;&gt;Shadcn&#x2F;UI&lt;&#x2F;a&gt;, ya que estos componentes ya implementan funcionalidades
con accesibilidad, como modales o barras laterales. Por ejemplo, la barra lateral que aparece cuando estás
en un dispositivo móvil y lo abres con el botón en el encabezado, con una animación suave.&lt;&#x2F;p&gt;
&lt;p&gt;Presentamos el proyecto a tiempo y estamos esperando los resultados.&lt;&#x2F;p&gt;
&lt;p&gt;Esta es la primera vez que participo en un Hackathon y realmente lo disfruté,
espero seguir contribuyendo al proyecto en GitHub.&lt;&#x2F;p&gt;
&lt;p&gt;Es increíble trabajar con personas de otros países, utilizando el inglés aunque no sea nuestra lengua materna,
pero con un propósito en común: crear un gran producto.&lt;&#x2F;p&gt;
&lt;p&gt;Seguiré buscando participar en más Hackathones en el futuro y contribuir a
proyectos de código abierto (open source) en Github, porque realmente disfruto la sensación de
desarrollar algo grande con más personas.&lt;&#x2F;p&gt;
&lt;p&gt;Aprendí mucho en estos pocos días, como la integración de Next.js con Supabase para autenticación y
rutas protegidas, utilizando la documentación de Supabase como guía, y utilizando por primera vez Shadcn&#x2F;UI,
y estoy ansioso por seguir usándolo.&lt;&#x2F;p&gt;
&lt;p&gt;Me llevó mucho tiempo participar en un Hackathon, ya que antes dudaba de mi experiencia,
pero la realidad es que nunca estaremos listos para nuevos desafíos, porque si ya estás listo,
significa que es demasiado tarde.&lt;&#x2F;p&gt;
&lt;p&gt;Quiero aprender más sobre el uso de vectores para la inteligencia artificial,
así que investigaré más sobre el tema, ya que la tecnología tiende a avanzar en esa dirección.&lt;&#x2F;p&gt;
&lt;p&gt;Quién sabe cuál será la próxima gran tendencia tecnológica o cuándo llegará.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;groce-wise.vercel.app&#x2F;&quot;&gt;Puedes ver el proyecto: Grocewise aquí&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="es">
        <title>La Monotonia de las Redes Sociales</title>
        <published>2023-07-17T00:00:00+00:00</published>
        <updated>2023-07-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Juan Manzanero
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://juanmanzanero.com/es/blog/la-monotonia-de-las-redes-sociales/"/>
        <id>https://juanmanzanero.com/es/blog/la-monotonia-de-las-redes-sociales/</id>
        
        <content type="html" xml:base="https://juanmanzanero.com/es/blog/la-monotonia-de-las-redes-sociales/">&lt;p&gt;Es evidente que muchas interacciones en Internet ocurren a través de las redes sociales,
lo que te permite enviar solicitudes de amistad, chatear o compartir memes y fotos.&lt;&#x2F;p&gt;
&lt;p&gt;Sin embargo, la abstracción de la interacción humana mediante software ha causado muchos
problemas que antes no existían.&lt;&#x2F;p&gt;
&lt;p&gt;Cuando empezó Internet, muchas personas crearon sus propios sitios web porque eso era lo
que debías tener si querías ser popular, y muchos de estos sitios web eran simplemente como
blogs donde los usuarios compartían sus pasatiempos como películas, deportes, libros, videojuegos, etc.&lt;&#x2F;p&gt;
&lt;p&gt;Esta motivación llevó a la creación de sitios web únicos y personales, donde podías conocer
a alguien y sus gustos o disgustos.&lt;&#x2F;p&gt;
&lt;p&gt;Ahora, con el auge de las redes sociales a mediados de los años 2000, la gente prefiere conectarse
simplemente buscando un nombre o viendo los amigos de sus amigos, y enviar solicitudes de amistad
para intentar conectarse. Al principio, esto era genial, como cualquier cosa nueva, pero los problemas
comenzaron cuando empresas como Facebook (ahora Meta) o Google (con YouTube) necesitaban
monetizar sus plataformas, principalmente con anuncios.&lt;&#x2F;p&gt;
&lt;p&gt;Y, por supuesto, eso significa que tenían que suprimir, censurar o prohibir cualquier cosa
que pudiera ser perjudicial para la sociedad, como discursos de odio o retos estúpidos que pudieran
poner en peligro la vida de las personas.&lt;&#x2F;p&gt;
&lt;p&gt;Pero lo malo de esto es que homogeneizan a casi todos, obligándolos a actuar como los algoritmos
recomiendan usuarios con gustos y comentarios similares, guiando a las personas a actuar como alguien más,
y así sucesivamente.&lt;&#x2F;p&gt;
&lt;p&gt;Ahora casi todos hacen principalmente dos cosas: publicar fotos sobre sus vidas “perfectas” o compartir memes,
y no me malinterpretes, está bien entrar en las redes sociales y tratar de desconectar de tu trabajo o problemas,
pero usarlas todos los días como una vía de escape instantánea en lugar de enfrentar tus propios
problemas podría ser perjudicial a largo plazo, aislándote de la necesidad de socializar en la vida real,
con personas reales, y pensando que todos tienen una vida perfecta.&lt;&#x2F;p&gt;
&lt;p&gt;No, TODOS tienen problemas en sus vidas, incluso más que los tuyos, pero los algoritmos de las
redes sociales promueven principalmente “solo vibraciones positivas” y todas esas tonterías que en grandes
cantidades son perjudiciales para nuestras mentes.&lt;&#x2F;p&gt;
&lt;p&gt;Y no mencionemos la censura y el shadow-banning si publicas algo controvertido, puede ser algo que no se
debería tolerar, como incitar al odio a un grupo, o puede ser algo en lo que no todos estén de acuerdo,
pero podría ser útil reflexionar un poco al respecto, aunque sea controvertido.&lt;&#x2F;p&gt;
&lt;p&gt;¿Debería todo el mundo poder expresar lo que piensa? Sí, siempre y cuando no promueva el odio o
lastime a otras personas o animales.&lt;&#x2F;p&gt;
&lt;p&gt;Hay informes de que Twitter promueve el odio en el algoritmo, y Meta sabe que Instagram aumenta la
ansiedad y la depresión en los jóvenes, y de hecho, lo promueve… ya que todos los sentimientos
negativos te mantienen en las redes sociales interactuando con los demás, ya que eso es lo que esas
empresas venden, tus datos y tu tiempo a los anunciantes.&lt;&#x2F;p&gt;
&lt;p&gt;Recientemente escuché un video que hablaba sobre este tema, y sería genial si volviéramos a los
inicios de Internet, donde las personas creaban contenido como un pasatiempo, en lugar de buscar
validación a través de los me gusta y los comentarios, siendo personas más auténticas en lugar de productos.&lt;&#x2F;p&gt;
&lt;p&gt;¿Deberían las soluciones de software reemplazar las interacciones humanas? Creo que no, pero es
demasiado tarde para casi todos, pero si estás leyendo esto, comienza por cambiar tu vida primero si
quieres ser honesto contigo mismo.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h6 id=&quot;blog-inspirado-en-why-does-every-personal-website-look-like-this-now-by-eric-murphy-en-youtube&quot;&gt;Blog inspirado en “Why does every personal website look like this now?” by Eric Murphy en YouTube.&lt;&#x2F;h6&gt;
&lt;p&gt;Video de origen (en Inglés):&lt;&#x2F;p&gt;
&lt;iframe
  width=&quot;560&quot;
  height=&quot;315&quot;
  src=&quot;https:&#x2F;&#x2F;www.youtube-nocookie.com&#x2F;embed&#x2F;_x6SCSz7g5I&quot;
  title=&quot;YouTube video player&quot;
  frameborder=&quot;0&quot;
  allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot;
  allowfullscreen
&gt;&lt;&#x2F;iframe&gt;
</content>
        
    </entry>
    <entry xml:lang="es">
        <title>Cómo Funcionan las Computadoras</title>
        <published>2023-05-29T00:00:00+00:00</published>
        <updated>2023-05-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Juan Manzanero
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://juanmanzanero.com/es/blog/como-funcionan-las-computadoras/"/>
        <id>https://juanmanzanero.com/es/blog/como-funcionan-las-computadoras/</id>
        
        <content type="html" xml:base="https://juanmanzanero.com/es/blog/como-funcionan-las-computadoras/">&lt;p&gt;Hoy en día usamos, de alguna manera, una computadora en casi toda actividad de nuestras vidas, podría ser para trabajo o solo por diversión, pero si lo pensamos con cuidado, las computadoras son un invento del siglo pasado, y han cambiado nuestras vidas.&lt;&#x2F;p&gt;
&lt;p&gt;Nuevos empleos han aparecido, nuevas carreras por estudiar, nuevos problemas por resolver.&lt;&#x2F;p&gt;
&lt;p&gt;Pero, ¿realmente sabemos cómo las computadoras penetraron en nuestras vidas?, ¿realmente sabemos cómo funciona una computadora?, ¿cómo funciona Internet?&lt;&#x2F;p&gt;
&lt;p&gt;Hay muchas personas que usan sus smartphones para comunicarse con su familia y amigos, para compartir sus vidas, pero no saben cómo es esto posible.&lt;&#x2F;p&gt;
&lt;p&gt;No estoy diciendo que todos deberían tener una Ingeniería en Software o ser expertos en IT, pero entender nuestra actual línea temporal puede ser un conocimiento destacable a tener.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;el-poder-de-las-computadoras&quot;&gt;El poder de las computadoras&lt;&#x2F;h2&gt;
&lt;p&gt;Las computadoras están hechas para expandir nuestros cerebros, desde cosas como enviar mensajes a personas del otro lado del planeta, hasta crear una app que agiliza un proceso de entrega.&lt;&#x2F;p&gt;
&lt;p&gt;Todo esto se hace tan solo “girando” unos y ceros (1´s y 0´s), pero, ¿cómo es esto posible?&lt;&#x2F;p&gt;
&lt;p&gt;Si ya has visto Él Código Enigma, ya sabes parte de esa historia.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fundamentos-de-computacion&quot;&gt;Fundamentos de Computación&lt;&#x2F;h3&gt;
&lt;p&gt;Alan Turing fue el inventor de la Máquina de Turing, una simple pero poderosa máquina que puede recibir instrucciones para moverse en una larga cinta, cambiando el estado de cada ranura. Estas 3 cosas, una cabeza, una cinta larga y un conjunto de instrucciones son las bases para las computadoras modernas.&lt;&#x2F;p&gt;
&lt;p&gt;La cabeza es la Unidad de Procesamiento Central (CPU), una pieza de hardware que puede ser usada con propósitos generales, recibiendo instrucciones (Algoritmo) que son convertidos a pulsos eléctricos, entendiendo si la electricidad pasa o no, si es verdadero o falso, 1 ó 0. Todas estas instrucciones son guardadas en la Memoria Aleatoria de Acceso, Random Access Memory (RAM) para un acceso rápido del trabajo que se ha hecho. La Memoria de Solo Lectura, Read-Only Memory (ROM) es usada para guardar datos persistentes que se necesitan recuperar aunque la computadora se apague.&lt;&#x2F;p&gt;
&lt;p&gt;Un algoritmo puede ser visto como una receta de cocina, declaras los ingredientes (variables) y los pasos a seguir (funciones) para obtener un resultado.&lt;&#x2F;p&gt;
&lt;p&gt;Una variable es un identificador que apunta a un slot en la RAM, guardando un valor que puede ser un número, un texto (se le conoce como “string”), un booleano (true o false), un object (un conjunto de múltiples variables y funciones que puede ser instanciado), etc.&lt;&#x2F;p&gt;
&lt;p&gt;Las funciones son bloques de instrucciones que cumplen una tarea, como obtener la ubicación actual o enviar un mensaje.&lt;&#x2F;p&gt;
&lt;p&gt;Y te estarás preguntando, ¿cómo puedo decirle a una computadora lo que quiero hacer?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;lenguajes-de-programacion&quot;&gt;Lenguajes de programación&lt;&#x2F;h3&gt;
&lt;p&gt;Si intentas hablar con alguien que no habla el mismo idioma que tu, intentas usar un traductor o usas gestos, algo que sabes que ambos puedan de alguna manera entender, lo mismo ocurre con las computadoras.&lt;&#x2F;p&gt;
&lt;p&gt;Las computadoras son poderosas, pero necesitan a alguien que les diga qué hacer, este es el trabajo de los humanos, y para conseguirlo usamos lenguajes de programación. Con un lenguaje de programación usas una sintaxis específica para decirle a la computadora lo que quieres hacer, una vez hecho esto compilas el archivo que contiene las instrucciones que escribiste, entonces es transformado a un lenguaje que las computadoras pueden entender con mayor facilitad (1´s y 0´s) y entonces la computadora realiza la tarea.&lt;&#x2F;p&gt;
&lt;p&gt;Hay diferentes lenguajes de programación, y todos ellos están diseñados para solucionar necesidades específicas. Lenguajes como C y C++ son lenguajes de bajo nivel, esto quiere decir que son muy cercanos a cómo una computadora “habla” y son usados para controlar y administrar la memoria en apps de alto rendimiento, o para iluminar las pantallas de tu computadora.&lt;&#x2F;p&gt;
&lt;p&gt;Existe Java, un lenguaje que crea un entorno cuando es compilado, lo que le permite ser utilizado en casi cualquier computadora.&lt;&#x2F;p&gt;
&lt;p&gt;JavaScript (NO tiene que ver con Java o algo así) es un lenguaje que los navegadores entienden, gracias a JavaScript podemos acceder a una página web y ver asombrosas interacciones cuando le damos click a un botón, inicar sesión con un username y password, y más.&lt;&#x2F;p&gt;
&lt;p&gt;JavaScript es un lenguaje de alto nivel, es más fácil de aprender que Java o C, pero no por eso es mejor o peor, simplemente resuelve una necesidad diferente.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;el-navegador&quot;&gt;El Navegador&lt;&#x2F;h3&gt;
&lt;p&gt;Es un poderoso software que puede acceder a otras computadoras usando el Hyper Text Transfer Protocol (HTTP), significa que gracias a este protocolo distintas computadoras pueden enviar y recibir información para comunicarse, incluso si están muy lejos. Los navegadores reciben datos en forma de archivos, principalmente tres:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Hyper Text Markup Language (HTML)&lt;&#x2F;li&gt;
&lt;li&gt;Cascading Styles Sheets (CSS)&lt;&#x2F;li&gt;
&lt;li&gt;JavaScript&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;html&quot;&gt;HTML&lt;&#x2F;h3&gt;
&lt;p&gt;Ayuda al navegador a estructurar los datos como textos o imágenes, usando un lenguaje de marcado (mediante tags). El navegador puede reconocer dónde hay un input para escribir nuestro email, o un botón para subscribirnos a nuestro artista favorito.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;css&quot;&gt;CSS&lt;&#x2F;h3&gt;
&lt;p&gt;Ayuda a colorear y formar los tags de HTML usando selectores. Puede ser usado para cambiar el color de fondo de la página, darle a un botón bordes redondeados, cambiar el color de texto, todo lo que tu creatividad pueda imaginar.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;javascript&quot;&gt;JavaScript&lt;&#x2F;h3&gt;
&lt;p&gt;Combinando HTML y CSS con Javascript se crea un sitio web interactivo, o aplicación web (como este). Puedes por ejemplo, añadir un botón que cambie el tema oscuro al claro, o guardar items en un carrito de compras y mostrar el número de items que tienes guardados.&lt;&#x2F;p&gt;
&lt;p&gt;Para todo esto necesitas guardar tus archivos en algún sitio, permitiéndole a las personas acceder a la computadora para descargar todos estos archivos usando sus navegadores, y para eso existen los servidores.&lt;&#x2F;p&gt;
&lt;p&gt;Los servidores son computadoras conectadas a Internet, guardan archivos que pueden ser descargados o subidos usando protocolos y reglas de seguridad. Algunas compañías como Google o Microsoft tienen múltiples centros con muchos servidores en diferentes regiones en el planeta, llamados Data Centers, y pueden ser usados por una tarifa para guardar tu web app, estos múltiples Data Centers se les conoce como Cloud.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;la-nube&quot;&gt;La Nube&lt;&#x2F;h3&gt;
&lt;p&gt;Administrar una poderosa computadora puede ser difícil, pero si sabes cómo usarla, puedes ahorrarte mucho dinero en lugar de mantener servidores locales que necesiten estar encendidos 24&#x2F;7. Gracias a la Cloud podemos entregar apps más rápidas, y podemos tener un servicio 24&#x2F;7 para nuestros clientes con un costo marginal.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;las-computadoras-cambiaron-la-humanidad&quot;&gt;Las Computadoras Cambiaron la Humanidad&lt;&#x2F;h2&gt;
&lt;p&gt;Las computadoras simplifican nuestras tareas diarias, el software puede ser fácilmente replicado y distribuido sin la necesidad de logística como si lo necesita un producto tangible. Solo necesitas una conexión a Internet para acceder al proyecto de alguien.&lt;&#x2F;p&gt;
&lt;p&gt;No necesitas una fábrica o un recurso natural como madera para producir papel, necesitas un grupo de ingenieros, diseñadores UX&#x2F;UI, digital marketers, y otras personas enfocadas en IT para alcanzar a miles de clientes.&lt;&#x2F;p&gt;
&lt;p&gt;La razón por la que las computadoras son tan poderosas es debido a que su costo marginal es mínimo, no necesitas extraer una materia prima de la tierra para construir una app, necesitas un grupo de personas talentosas que usen sus cerebros para crear soluciones.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="es">
        <title>La Razón para Crear una Versión 2.0 de mi Website</title>
        <published>2023-04-07T00:00:00+00:00</published>
        <updated>2023-04-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Juan Manzanero
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://juanmanzanero.com/es/blog/website-2-0/"/>
        <id>https://juanmanzanero.com/es/blog/website-2-0/</id>
        
        <content type="html" xml:base="https://juanmanzanero.com/es/blog/website-2-0/">&lt;p&gt;La primera versión de mi sitio web fue uno de mis mayores proyectos hasta ahora, pero ahora que tengo más experiencia como ingeniero Frontend, me doy cuenta de que no hice suficiente investigación para crear un sitio web con un blog.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;renderizacion-del-lado-del-cliente-csr-vs-renderizacion-del-lado-del-servidor-ssr-vs-generacion-estatica-sg&quot;&gt;Renderización del lado del cliente (CSR) vs. Renderización del lado del servidor (SSR) vs. Generación estática (SG)&lt;&#x2F;h2&gt;
&lt;p&gt;Al &lt;strong&gt;desarrollar&lt;&#x2F;strong&gt; una aplicación &lt;strong&gt;web&lt;&#x2F;strong&gt;, debes &lt;strong&gt;pensar&lt;&#x2F;strong&gt; en el tipo de &lt;strong&gt;renderizado&lt;&#x2F;strong&gt; que se debe utilizar, teniendo en cuenta los &lt;strong&gt;requisitos&lt;&#x2F;strong&gt; de los &lt;strong&gt;problemas&lt;&#x2F;strong&gt; que deseas &lt;strong&gt;resolver&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;generacion-del-lado-del-cliente-csg&quot;&gt;Generación del lado del cliente (CSG)&lt;&#x2F;h3&gt;
&lt;p&gt;Por ejemplo, una aplicación &lt;strong&gt;web&lt;&#x2F;strong&gt; como una &lt;strong&gt;SaaS&lt;&#x2F;strong&gt; para crear tareas y administrar personas tendrá &lt;strong&gt;páginas dinámicas&lt;&#x2F;strong&gt; para mostrar las tareas, actualizar las tarjetas cuando se editan o eliminan, mostrar notificaciones, etc. En esta situación, un &lt;strong&gt;CSR&lt;&#x2F;strong&gt; sería &lt;strong&gt;mejor&lt;&#x2F;strong&gt; para &lt;strong&gt;renderizar&lt;&#x2F;strong&gt; la página &lt;strong&gt;cada&lt;&#x2F;strong&gt; vez que el usuario &lt;strong&gt;solicita&lt;&#x2F;strong&gt; acceso para &lt;strong&gt;mantener&lt;&#x2F;strong&gt; los datos &lt;strong&gt;actualizados&lt;&#x2F;strong&gt;. Sin embargo, un &lt;strong&gt;CSR&lt;&#x2F;strong&gt; necesita &lt;strong&gt;hidratar&lt;&#x2F;strong&gt; la página cuando se &lt;strong&gt;solicita&lt;&#x2F;strong&gt;, lo que provoca una &lt;strong&gt;carga lenta&lt;&#x2F;strong&gt; al &lt;strong&gt;principio&lt;&#x2F;strong&gt; y utiliza &lt;strong&gt;más recursos&lt;&#x2F;strong&gt; de la PC del usuario.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;renderizacion-del-lado-del-servidor-ssr&quot;&gt;Renderización del lado del servidor (SSR)&lt;&#x2F;h3&gt;
&lt;p&gt;Esto podría resolverse utilizando &lt;strong&gt;SSR&lt;&#x2F;strong&gt;, que consiste en &lt;strong&gt;generar&lt;&#x2F;strong&gt; la página en el &lt;strong&gt;servidor&lt;&#x2F;strong&gt; donde se aloja la aplicación web utilizando toda la &lt;strong&gt;potencia&lt;&#x2F;strong&gt; que un &lt;strong&gt;servidor&lt;&#x2F;strong&gt; puede proporcionar. El problema es que se requiere un &lt;strong&gt;servidor&lt;&#x2F;strong&gt;. Google Cloud proporciona opciones sin servidor como App Engine o Cloud Run, pero deberás aprender sobre estos servicios y cómo implementar el
proyecto, por lo que el &lt;strong&gt;conocimiento técnico&lt;&#x2F;strong&gt; es &lt;strong&gt;alto&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;La &lt;strong&gt;desventaja&lt;&#x2F;strong&gt; de &lt;strong&gt;CSG&lt;&#x2F;strong&gt; y &lt;strong&gt;SSR&lt;&#x2F;strong&gt; es que, debido a que &lt;strong&gt;cada página&lt;&#x2F;strong&gt; debe ser &lt;strong&gt;renderizada&lt;&#x2F;strong&gt; en cada &lt;strong&gt;solicitud&lt;&#x2F;strong&gt;, los &lt;strong&gt;rastreadores web&lt;&#x2F;strong&gt; y los &lt;strong&gt;motores de búsqueda&lt;&#x2F;strong&gt; como Google tardarán más tiempo en obtener información sobre tu página, lo que resultará en una &lt;strong&gt;prioridad SEO&lt;&#x2F;strong&gt; baja.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;generacion-estatica-sg&quot;&gt;Generación estática (SG)&lt;&#x2F;h3&gt;
&lt;p&gt;Bueno, si una &lt;strong&gt;página&lt;&#x2F;strong&gt; no requiere &lt;strong&gt;buscar&lt;&#x2F;strong&gt; datos para &lt;strong&gt;cada solicitud&lt;&#x2F;strong&gt;, entonces puedes usar &lt;strong&gt;SG&lt;&#x2F;strong&gt;, lo que significa que la &lt;strong&gt;página&lt;&#x2F;strong&gt; se &lt;strong&gt;genera&lt;&#x2F;strong&gt; cuando construyes el directorio de producción &lt;strong&gt;antes&lt;&#x2F;strong&gt; de implementarlo. La página se generará en HTML&#x2F;CSS&#x2F;JS una vez, y si necesitas &lt;strong&gt;actualizar&lt;&#x2F;strong&gt; los datos en esa página, deberás hacer los cambios y &lt;strong&gt;implementar&lt;&#x2F;strong&gt; el proyecto. Sí, deberás ser más cuidadoso al revisar los cambios antes de implementarlos, pero como la página ya está generada, los &lt;strong&gt;rastreadores web&lt;&#x2F;strong&gt; y el &lt;strong&gt;motor de búsqueda&lt;&#x2F;strong&gt; de Google obtendrán la &lt;strong&gt;información&lt;&#x2F;strong&gt; de tu página &lt;strong&gt;más rápido&lt;&#x2F;strong&gt;, lo que &lt;strong&gt;mejorará&lt;&#x2F;strong&gt; tu &lt;strong&gt;SEO&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lo-interesante-de-next-js&quot;&gt;Lo interesante de Next.js&lt;&#x2F;h2&gt;
&lt;p&gt;En el &lt;strong&gt;pasado&lt;&#x2F;strong&gt;, se requería &lt;strong&gt;pensar&lt;&#x2F;strong&gt; si usar CSG completo, SSR o SG, vinculando tu sitio web a sus respectivas secciones, como la aplicación, el blog, etc.
&lt;strong&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;nextjs.org&#x2F;&quot;&gt;Next.js&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt; es un &lt;strong&gt;meta-framework&lt;&#x2F;strong&gt; de &lt;strong&gt;Node.js&lt;&#x2F;strong&gt; que utiliza &lt;strong&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;react.dev&#x2F;&quot;&gt;React.js&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt; para construir la interfaz de usuario, y proporciona CSR, SSR, SG y más, por lo que puedes generar SG obteniendo datos de forma asíncrona, lo que te permite no tener que crear cada página estática.&lt;&#x2F;p&gt;
&lt;p&gt;Este es el &lt;strong&gt;enfoque&lt;&#x2F;strong&gt; utilizado para este sitio web, en lugar de obtener los datos en cada solicitud, solo obtengo los datos cuando creo la construcción del proyecto.&lt;&#x2F;p&gt;
&lt;p&gt;Cada artículo es una página SG, pero utilizo una &lt;strong&gt;plantilla&lt;&#x2F;strong&gt; para mantener todos los blogs similares, utilizando la sintaxis &lt;strong&gt;markdown&lt;&#x2F;strong&gt; para el contenido del blog, y con una &lt;strong&gt;extensión&lt;&#x2F;strong&gt; de &lt;strong&gt;TailwindCSS&lt;&#x2F;strong&gt; mantengo los estilos consistentes.&lt;&#x2F;p&gt;
&lt;p&gt;Así que, Next.js te permite elegir el método de renderizado para cada página de tu sitio web, esta característica permite crear sitios web increíbles en el mismo proyecto, manteniendo la coherencia y con tiempos de carga rápidos, Next.js incluso carga de forma perezosa cada página y comienza a cargarse cuando pasas el cursor sobre un enlace como Inicio, Contacto, etc.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;¡Explicaré en más detalle la arquitectura de este proyecto en el futuro!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;tailwindcss-vs-mui&quot;&gt;&lt;strong&gt;TailwindCSS vs MUI&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Elegí usar &lt;strong&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tailwindcss.com&#x2F;&quot;&gt;TailwindCSS&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt; para aprender sobre esta biblioteca CSS, y estoy impresionado por lo &lt;strong&gt;rápido&lt;&#x2F;strong&gt; que hace el desarrollo de los estilos de un proyecto web. &lt;strong&gt;MUI&lt;&#x2F;strong&gt; proporciona &lt;strong&gt;funcionalidades&lt;&#x2F;strong&gt;, pero a veces &lt;strong&gt;presenta problemas&lt;&#x2F;strong&gt; con la &lt;strong&gt;hidratación&lt;&#x2F;strong&gt;, como en mi sitio web anterior, cuando &lt;strong&gt;cargas&lt;&#x2F;strong&gt; la página por primera vez, tarda un &lt;strong&gt;tiempo&lt;&#x2F;strong&gt; en &lt;strong&gt;mostrar&lt;&#x2F;strong&gt; todos los &lt;strong&gt;estilos&lt;&#x2F;strong&gt;, ahora ya no ocurre porque TailwindCSS es CSS puro y las páginas son estáticas.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;despliegue-en-vercel&quot;&gt;Despliegue en Vercel&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;vercel.com&#x2F;&quot;&gt;Vercel&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt; es la empresa detrás de Next.js, y ofrece servicios de &lt;strong&gt;hosting&lt;&#x2F;strong&gt; optimizados para aplicaciones &lt;strong&gt;Node.js&lt;&#x2F;strong&gt;, y como estoy aprendiendo sobre desarrollo en la nube, tal vez podría intentar alojar esta web en un servicio en la nube como Cloud Run, pero esta vez elijo utilizar &lt;strong&gt;Vercel&lt;&#x2F;strong&gt; para obtener las &lt;strong&gt;analíticas&lt;&#x2F;strong&gt; que son muy útiles, y como el &lt;strong&gt;plan hobby&lt;&#x2F;strong&gt; me ofrece alojamiento gratuito para proyectos pequeños.&lt;&#x2F;p&gt;
&lt;p&gt;Para implementar, utilizo la &lt;strong&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;vercel.com&#x2F;docs&#x2F;cli&quot;&gt;Vercel CLI&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;, bastante simple y directo.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mas-contenido-en-camino&quot;&gt;¡Más contenido en camino!&lt;&#x2F;h2&gt;
&lt;p&gt;Seguiré actualizando con publicaciones, características y más contenido para compartir mi experiencia, y ahora que estoy escribiendo este párrafo, creo que la próxima característica podría ser un boletín para notificar a las personas cuando creo una nueva publicación. ¡Hora de trabajar!&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
