Lecciones del curso
Aprende Remix construyendo un Blog con MongoDB y Netlify
Maquetando el dashboard
Es hora de crear una de las secciones más interesantes de este proyecto, me refiero al panel de administración.
Regularmente conocido como dashboard, aunque este nombre implicaría también la visualización de datos, el nuestro será en extremo minimalista, pero no por ello le faltará alguna funcionalidad, todo lo contrario, nuestro dashboard minimalista tendrá lo necesario para crear nuevos posts, editarlos, publicarlos o eliminarlos.
¡El CRUD completo! 🪅
Definiendo la ruta
Vamos a escribir la ruta /dash.tsx
que mostrará una lista de posts de blog.
Esta ruta tiene dos funciones: mostrar una lista con todos nuestros posts; y detonar la creación de un nuevo post.
El loader entregará la lista de posts que conseguirá de la base de datos al cliente.
Mientras que el action creará un nuevo post en la base de datos y luego redireccionará a la ruta de edición utilizando el dato del post recién creado.
Habrá un botón para agregar un nuevo post, también, si damos clic a alguno, podremos editarlo en la correspondiente ruta dinámica para el detalle que crearemos en la siguiente lección.
Creamos entonces dash.ts
y dibujamos el mínimo JSX para nuestro dashboard minimalista.
Con esto, estamos listos(as) para trabajar en el cliente.
Escribiendo el cliente
Nuestro dashboard consta de una barra de navegación <nav>
que incluye el titulo y el call to action (el form y el botón) y también de un <section>
que funciona como contenedor de todos nuestros posts, utilizando el componente <PostCard>
.
// app/routes/dash.tsx import { Form } from "@remix-run/react"; import { PostCard } from "~/components/PostCard"; export default function Dash() { return ( <> <nav className="flex justify-between items-center max-w-3xl mx-auto"> <div> <h2 className="block text-3xl font-bold mt-4 px-4 text-left"> Todos tus posts </h2> <p className="px-4 text-md font-thin">Administra tu blog</p> </div> <Form method="post"> <button name="intent" value="new-post" className="text-white mr-4 bg-indigo-500 py-2 px-8 rounded-md hover:bg-indigo-600" > Crear nuevo + </button> </Form> </nav> <section className="max-w-3xl mx-auto flex flex-wrap gap-2 py-20 "> {posts.map((p) => ( <PostCard id={p.id} title={p.title} key={p.id} /> ))} </section> </>
Toma nota del <Form>
de Remix que hemos incluido.
Form nos permitirá consumir el action sin refrescar la página. También nota que el botón tiene la propiedad name como intent y en la propiedad value: new-post.
Con esto estamos indicando lo que la función action debe hacer.
Leyendo PostCard
Antes de ir al backend, quiero mostrarte un componente que he creado para mostrar cada post de la lista.
import { Link } from "@remix-run/react"; export const PostCard = ({ title, id, }: { id: string; title: string | null; }) => { return ( <Link to={`${id}/edit`} className="p-4 rounded-md border border-indigo-500 w-[300px] hover:ring-indigo-500 hover:ring " > {title} </Link> ); };
Este componente devuelve un <Link>
que apunta a la ruta dinámica que se encargará de la edición y que crearemos más adelante.
Escribiendo el loader
Vamos a entregar a esta ruta una lista de todos nuestros posts usando el loader.
import { type LoaderArgs } from "@remix-run/node"; export const loader = async ({ request }:LoaderArgs) => { const posts = await db.post.findMany(); return { posts }; };
Me encantan los loaders 🙈, yo utilicé Redux por varios años, incluso tengo un curso en Udemy sobre Redux, con miles de estudiantes y excelente calificación, pero cada vez que tenía que usar Redux, sufría.
El loader de Remix me devolvió las ganas de construir aplicaciones web. 😚
Gracias Remix. 🫶🏻
Escribiendo el action
Vamos ahora al action, es importante decir que en este proyecto voy a seguir un pattern que he querido experimentar:
Antes de redireccionar al usuario, quiero crear el post vacío, y con esto ahorrarme una vista, la vista de “new”, todo lo administrará una sola vista, la de “edit”.
// app/routes/dash.tsx export const action: ActionFunction = async ({ request }) => { const formData = await request.formData(); const intent = formData.get("intent"); // Usamos el intent para multiples acciones if (intent === "new-post") { // Creamos el post const post = await db.post.create({ data: { userId: "648ce85a2e43deef9f5b7a87", // @RETO Cámbia por real // Colocamos algunos defaults title: "Sin nombre", slug: slugify("sin-nombre" + Date.now()), }, }); // Redireccionamos al cliente, // usando el slug del nuevo post throw redirect(`/dash/${post.slug}/edit`); } return null; };
AUMENTAR DESCRIBIENDO EL CÓDIGO
La experiencia del usuario con este pattern me parece más que aceptable, interesante, tenía ganas de probarla. ¿tú qué opinas?
En resumen
Con la función action recibimos la petición del usuario para crear un post nuevo y lo creamos por él, con un nombre genérico, sin olvidar asignar al usuario actual como autor, para después redireccionarlo a la edición de este nuevo post. Este pattern me gusta, pues me ayuda a conseguir un dashboard minimalista. 🤓
“The definition of genius is taking the complex and making it simple.”
―
Albert Einstein
Ahora tenemos una forma de crear un post directamente en la base de datos, con un simple clic y gracias a que tenemos una sesión de usuario abierta, también sabemos a quién asignarle el nuevo post. 👏🏼
Ahora pasemos a la ruta dinámica de la edición. 🔥