township-agency / ex_teal

MIT License
1 stars 0 forks source link

Dashboard Resource Table, Virtual Aggregates #189

Closed staylorwr closed 1 year ago

staylorwr commented 1 year ago

What is this?

Teal can how show resource index tables on the dashboard. Not only that, teal can now show deeply customized fields and aggregates on resource indexes! Take a look at the loom below to see whats going on:

https://www.loom.com/share/964d6bd6fae84e3ba95cf02e660c91e0

The full code for the resource shown above is copied here:

defmodule VoyagerWeb.ExTeal.Dashboard.ProgramListItem do
  use Ecto.Schema

  schema "programs" do
    field :name, :string
    field :total_mentors, :integer, virtual: true
    field :total_mentees, :integer, virtual: true
    field :program_year, :integer, virtual: true
    field :session_count, :integer, virtual: true
    field :start_on, :date
    field :status, :string
    field :identifier, :string, virtual: true
  end
end

defmodule VoyagerWeb.ExTeal.Dashboard.ProgramListResource do
  use ExTeal.Resource
  import Ecto.Query
  alias ExTeal.Fields.{Text, Number}
  alias ExTeal.Field
  alias VoyagerWeb.ExTeal.ReadOnlyPolicy
  alias VoyagerWeb.ExTeal.Dashboard.ProgramListItem

  @impl true
  def records(conn, _resource) do
    from(
      p in ProgramListItem,
      left_join: m in "mentors_programs",
      on: m.program_id == p.id,
      left_join: n in "mentee_progress",
      on: n.program_id == p.id,
      left_join: s in "sessions",
      on: s.program_id == p.id,
      where: p.id in ^conn.assigns.auth.program_ids,
      where: p.status == "active",
      group_by: p.id,
      select_merge: %{
        total_mentors: m.contact_id |> count(:distinct) |> selected_as(:total_mentors),
        total_mentees: n.contact_id |> count(:distinct) |> selected_as(:total_mentees),
        session_count: s.id |> count() |> selected_as(:session_count),
        program_year:
          fragment("date_part('year', ?)::integer", p.start_on) |> selected_as(:program_year)
      }
    )
  end

  @impl true
  def fields,
    do: [
      Text.make(:name),
      Number.make(:total_mentors) |> Field.virtual(),
      Number.make(:total_mentees) |> Field.virtual(),
      Number.make(:program_year) |> Field.virtual(),
      Number.make(:session_count) |> Field.virtual(),
      Text.make(:status)
    ]

  @impl true
  def hide_from_nav, do: true

  @impl true
  def policy, do: ReadOnlyPolicy

  @impl true
  def navigate_to(_, _conn, program_list_item),
    do: %{
      resource_name: "programs",
      resource_id: program_list_item.id
    }

  @impl true
  def title, do: "Active Program Overview"
end

Then on a dashboard, we can show the resource:

defmodule VoyagerWeb.ExTeal.ProgramAdvisorDashboard do
  use ExTeal.Dashboard

  @impl true
  def cards(_),
    do: [
      VoyagerWeb.ExTeal.ProgramListCard,
      VoyagerWeb.ExTeal.ProgramAdvisorParticipantsCard
    ]

  @impl true
  def display?(conn) do
    conn.assigns.auth.role == :program_advisor
  end

  @impl true
  def title, do: "My Dashboard"
end

where ProgramListCard implements the new ExTeal.Cards.ResourceIndex behavior:

defmodule VoyagerWeb.ExTeal.ProgramListCard do
  use ExTeal.Cards.ResourceIndex

  @impl true
  def resource, do: VoyagerWeb.ExTeal.Dashboard.ProgramListResource

  @impl true
  def width, do: "full"
end