Actividad reciente

"Pedidos recientes" del panel con el lenguaje del sistema: iniciales, título, cuándo pasó, monto alineado y la pastilla de estado. Lo que llega por Turbo Stream entra subiendo, se ilumina un momento y se anuncia a lectores de pantalla. Recibe ítems genéricos (hashes).

app/views/… (la demo de arriba)

<%# Cada fila es un hash: quién, referencia, hora, monto y estado. En tu app un
    adaptador arma esos hashes desde tus modelos, sin consultas en la vista. %>
<% now = Time.current %>
<% pedido = ->(id, title, ago, amount, status = "completed") do
     { id: "activity_order_#{id}", reference: "##{id}", title:, time: now - ago, amount:, status:,
       status_label: { "completed" => "Completado", "cancelled" => "Cancelado" }[status], href: "#pedido-#{id}" }
   end %>
<% pedidos = [
     pedido.(36, "Mariana Peña", 3.hours, 1300),
     pedido.(35, "Diego Hernández", 5.hours, 2079),
     pedido.(34, "invitada@correo.mx", 1.day, 340),
     pedido.(33, "Lucía Torres", 2.days, 890.5, "cancelled")
   ] %>
<% nuevo = pedido.(37, "Sofía Ruiz", 0.seconds, 1650) %>

<div class="flex w-full max-w-md flex-col items-center gap-4" data-controller="nested-form">
  <%= render Ui::ActivityFeedComponent.new(items: pedidos, title: "Pedidos recientes", href: "#pedidos", id: "activity-feed-demo",
                                           live: true, limit: 5, class: "w-full") %>

  <%# En la app el servidor manda este turbo_stream.prepend (p. ej. al completarse el pago);
      aquí el botón lo inyecta para ver cómo entra la fila. Con el mismo id, Turbo
      reemplaza la fila en vez de duplicarla. %>
  <template data-nested-form-target="template">
    <turbo-stream action="prepend" target="activity-feed-demo-list">
      <template><%= render Ui::ActivityFeedComponent::ItemComponent.new(**nuevo) %></template>
    </turbo-stream>
  </template>
  <div data-nested-form-target="items" hidden></div>
  <%= render Ui::ButtonComponent.new(variant: :secondary, size: :sm, data: { action: "nested-form#add" }) do %>Simular pedido nuevo<% end %>
</div>

Qué hace

  • La lista tiene id «<id>-list»: el destino de turbo_stream.prepend.
  • Las filas nuevas entran con animate-rise y un destello breve; las que ya estaban no se mueven.
  • Montos como $1,350 (centavos solo si los hay) con cifras tabulares.
  • Fechas relativas en español («hace 3 horas», «ayer») con la fecha completa al pasar.

Props

Nombre Tipo Default Descripción
title / href / href_label String — / nil / "Ver todos" Encabezado y el enlace a la lista completa.
items Array<Hash> [] Ítems genéricos: title, reference, initials, time, meta, amount, status, status_label, href, id. También con el slot with_item(…).
ItemComponent.new(**item) componente — La fila suelta desde un ítem, para el turbo_stream.prepend a «<id>-list».
limit Integer nil Máximo de filas; al llegar una nueva sale la más vieja.
live Boolean false Pastilla «En vivo» junto al título (cuando la vista escucha un stream).
empty_title / empty_description String "Sin actividad todavía" El estado vacío; se va solo cuando llega la primera fila.

Accesibilidad