From 8ce74c06a2eecac821bb2cdffc703568f02048de Mon Sep 17 00:00:00 2001 From: Alex Lion Date: Mon, 6 Apr 2026 16:48:46 +0200 Subject: [PATCH] Add pagination tests for users and events in Admin module --- lib/claper/accounts/user.ex | 13 + lib/claper/admin.ex | 110 +----- lib/claper/audit/log.ex | 2 + lib/claper/events/event.ex | 22 ++ lib/claper_web/components/core_components.ex | 80 +++++ .../live/admin_live/audit_log_live.html.heex | 65 ++-- lib/claper_web/live/admin_live/event_live.ex | 143 ++------ .../live/admin_live/event_live.html.heex | 328 +++++------------- lib/claper_web/live/admin_live/user_live.ex | 165 ++------- .../live/admin_live/user_live.html.heex | 303 +++++----------- .../live/event_live/event_card_component.ex | 109 ++++-- lib/claper_web/live/event_live/index.ex | 2 +- .../live/event_live/manage.html.heex | 6 +- .../manage_interaction_list_component.ex | 147 ++++++-- priv/gettext/default.pot | 306 +++++++++++----- test/claper/admin_test.exs | 93 +++++ 16 files changed, 878 insertions(+), 1016 deletions(-) create mode 100644 test/claper/admin_test.exs diff --git a/lib/claper/accounts/user.ex b/lib/claper/accounts/user.ex index 2262642..b1484fb 100644 --- a/lib/claper/accounts/user.ex +++ b/lib/claper/accounts/user.ex @@ -3,6 +3,19 @@ defmodule Claper.Accounts.User do import Ecto.Changeset + @derive { + Flop.Schema, + max_limit: 100, + default_limit: 20, + pagination_types: [:page], + filterable: [:email, :role_id], + sortable: [:email, :role_id, :confirmed_at, :inserted_at], + default_order: %{ + order_by: [:inserted_at], + order_directions: [:desc] + } + } + @type t :: %__MODULE__{ id: integer(), uuid: Ecto.UUID.t(), diff --git a/lib/claper/admin.ex b/lib/claper/admin.ex index 96301f3..9d27f7a 100644 --- a/lib/claper/admin.ex +++ b/lib/claper/admin.ex @@ -316,128 +316,46 @@ defmodule Claper.Admin do end @doc """ - Returns a paginated list of users. + Returns a paginated list of users using Flop. - ## Options - - * `:page` - The page number (default: 1) - * `:per_page` - The number of users per page (default: 20) - * `:search` - Search term for filtering users by email - * `:role` - Filter users by role name + Accepts Flop params for filtering, sorting, and pagination. + See `Claper.Accounts.User` Flop.Schema config for available fields. ## Examples - iex> list_users_paginated(%{page: 1, per_page: 10}) - %{entries: [%User{}, ...], page_number: 1, page_size: 10, total_entries: 20, total_pages: 2} + iex> list_users_paginated(%{"page" => 1, "page_size" => 10}) + {[%User{}, ...], %Flop.Meta{}} """ def list_users_paginated(params \\ %{}) do - page = Map.get(params, "page", 1) - per_page = Map.get(params, "per_page", 20) - search = Map.get(params, "search", "") - role = Map.get(params, "role", "") - query = User |> where([u], is_nil(u.deleted_at)) |> preload(:role) + |> join(:left, [u], r in assoc(u, :role), as: :role) - query = - if search != "" do - query |> where([u], ilike(u.email, ^"%#{search}%")) - else - query - end - - query = - if role != "" do - query |> join(:inner, [u], r in assoc(u, :role), on: r.name == ^role) - else - query - end - - query = query |> order_by([u], desc: u.inserted_at) - - Repo.paginate(query, page: page, page_size: per_page) + Flop.validate_and_run!(query, params, for: User, replace_invalid_params: true) end @doc """ - Returns a paginated list of events. + Returns a paginated list of events using Flop. - ## Options - - * `:page` - The page number (default: 1) - * `:per_page` - The number of events per page (default: 20) - * `:search` - Search term for filtering events by name - * `:status` - Filter events by status (upcoming, past) - * `:start_date` - Filter events by start date - * `:end_date` - Filter events by end date - * `:creator_id` - Filter events by creator ID + Accepts Flop params for filtering, sorting, and pagination. + See `Claper.Events.Event` Flop.Schema config for available fields. ## Examples - iex> list_events_paginated(%{page: 1, per_page: 10}) - %{entries: [%Event{}, ...], page_number: 1, page_size: 10, total_entries: 20, total_pages: 2} + iex> list_events_paginated(%{"page" => 1, "page_size" => 10}) + {[%Event{}, ...], %Flop.Meta{}} """ def list_events_paginated(params \\ %{}) do - page = Map.get(params, "page", 1) - per_page = Map.get(params, "per_page", 20) - search = Map.get(params, "search", "") - status = Map.get(params, "status", "") - start_date = Map.get(params, "start_date", nil) - end_date = Map.get(params, "end_date", nil) - creator_id = Map.get(params, "creator_id", nil) - query = Event + |> join(:left, [e], u in assoc(e, :user), as: :user) |> preload(:user) - query = - if search != "" do - query |> where([e], ilike(e.name, ^"%#{search}%")) - else - query - end - - query = - case status do - "upcoming" -> - now = NaiveDateTime.utc_now() - query |> where([e], e.started_at > ^now) - - "past" -> - now = NaiveDateTime.utc_now() - query |> where([e], e.started_at <= ^now) - - _ -> - query - end - - query = - if start_date do - query |> where([e], e.started_at >= ^start_date) - else - query - end - - query = - if end_date do - query |> where([e], e.started_at <= ^end_date) - else - query - end - - query = - if creator_id do - query |> where([e], e.user_id == ^creator_id) - else - query - end - - query = query |> order_by([e], desc: e.started_at) - - Repo.paginate(query, page: page, page_size: per_page) + Flop.validate_and_run!(query, params, for: Event, replace_invalid_params: true) end @doc """ diff --git a/lib/claper/audit/log.ex b/lib/claper/audit/log.ex index fef6720..ca5e4c4 100644 --- a/lib/claper/audit/log.ex +++ b/lib/claper/audit/log.ex @@ -5,6 +5,8 @@ defmodule Claper.Audit.Log do @derive { Flop.Schema, max_limit: 100, + default_limit: 20, + pagination_types: [:page], filterable: [:action, :user_email], sortable: [:inserted_at, :action], default_order: %{ diff --git a/lib/claper/events/event.ex b/lib/claper/events/event.ex index d1f3091..59f75d4 100644 --- a/lib/claper/events/event.ex +++ b/lib/claper/events/event.ex @@ -2,6 +2,28 @@ defmodule Claper.Events.Event do use Ecto.Schema import Ecto.Changeset + @derive { + Flop.Schema, + max_limit: 100, + default_limit: 20, + pagination_types: [:page], + filterable: [:name, :code, :user_email], + sortable: [:name, :code, :started_at, :expired_at, :audience_peak], + default_order: %{ + order_by: [:started_at], + order_directions: [:desc] + }, + adapter_opts: [ + join_fields: [ + user_email: [ + binding: :user, + field: :email, + path: [:user, :email] + ] + ] + ] + } + @type t :: %__MODULE__{ id: integer(), uuid: Ecto.UUID.t(), diff --git a/lib/claper_web/components/core_components.ex b/lib/claper_web/components/core_components.ex index f3d98b8..ce1f14a 100644 --- a/lib/claper_web/components/core_components.ex +++ b/lib/claper_web/components/core_components.ex @@ -495,4 +495,84 @@ defmodule ClaperWeb.CoreComponents do def translate_errors(errors, field) when is_list(errors) do for {^field, {msg, opts}} <- errors, do: translate_error({msg, opts}) end + + @doc """ + Renders a DaisyUI pagination component from a Flop.Meta struct. + + ## Attributes + + * `meta` - A `Flop.Meta` struct with pagination info. + * `path` - The base path for pagination links. + """ + attr :meta, :map, required: true + attr :path, :string, required: true + + def pagination(assigns) do + meta = assigns.meta + + assigns = + assigns + |> assign(:current_page, meta.current_page || 1) + |> assign(:total_pages, meta.total_pages || 1) + |> assign(:has_previous?, meta.has_previous_page?) + |> assign(:has_next?, meta.has_next_page?) + |> assign(:page_range, page_range(meta.current_page || 1, meta.total_pages || 1)) + + ~H""" + + """ + end + + defp page_path(path, meta, page) do + Flop.Phoenix.build_path(path, %{meta | flop: %{meta.flop | page: page}}) + end + + defp page_range(_current, total) when total <= 5, do: 1..total + + defp page_range(current, total) do + start = max(1, current - 2) + stop = min(total, start + 4) + start = max(1, stop - 4) + start..stop + end end diff --git a/lib/claper_web/live/admin_live/audit_log_live.html.heex b/lib/claper_web/live/admin_live/audit_log_live.html.heex index b12fd30..2ded133 100644 --- a/lib/claper_web/live/admin_live/audit_log_live.html.heex +++ b/lib/claper_web/live/admin_live/audit_log_live.html.heex @@ -1,49 +1,22 @@ -

{@page_title}

+

{@page_title}

<%= if @live_action == :index do %> -
- <.form - for={@form} - class="grow flex flex-col sm:flex-row gap-3 sm:[&>div]:basis-[250px]" - phx-change="filter-logs" - > - - - - - *]:rounded-l-full last:[&>*]:rounded-r-full text-gray-500 bg-white [&>*]:grid [&>*]:place-content-center" - ]} - page_link_attrs={[ - class: "hover:text-black hover:bg-gray-200" - ]} - current_page_link_attrs={[ - class: "pointer-events-none font-bold text-black" - ]} - > - <:previous attrs={[ - class: - "order-1 border border-gray-300 rounded-l-full h-10 p-5 bg-white flex items-center justify-center aria-disabled:text-gray-300 hover:not-aria-disabled:bg-gray-200" - ]}> - {gettext("Previous")} - - <:next attrs={[ - class: - "order-2 border-t border-r border-b border-gray-300 rounded-r-full h-10 p-5 bg-white flex items-center justify-center aria-disabled:text-gray-300 hover:not-aria-disabled:bg-gray-200" - ]}> - {gettext("Next")} - - <:ellipsis> - - - -
+ <.form + for={@form} + class="flex flex-col sm:flex-row gap-3 sm:[&>div]:basis-[250px]" + phx-change="filter-logs" + > + + + + + + +
+ +
<% end %> <%= if @live_action == :show do %> diff --git a/lib/claper_web/live/admin_live/event_live.ex b/lib/claper_web/live/admin_live/event_live.ex index cc65890..c8f46ea 100644 --- a/lib/claper_web/live/admin_live/event_live.ex +++ b/lib/claper_web/live/admin_live/event_live.ex @@ -3,7 +3,6 @@ defmodule ClaperWeb.AdminLive.EventLive do alias Claper.Admin alias Claper.Events.Event - alias ClaperWeb.Helpers.CSVExporter @impl true def mount(_params, session, socket) do @@ -11,12 +10,7 @@ defmodule ClaperWeb.AdminLive.EventLive do Gettext.put_locale(ClaperWeb.Gettext, locale) end - {:ok, - socket - |> assign(:page_title, gettext("Events")) - |> assign(:events, list_events()) - |> assign(:search, "") - |> assign(:current_sort, %{field: :na, order: :asc})} + {:ok, socket} end @impl true @@ -24,10 +18,25 @@ defmodule ClaperWeb.AdminLive.EventLive do {:noreply, apply_action(socket, socket.assigns.live_action, params)} end - defp apply_action(socket, :index, _params) do + defp apply_action(socket, :index, params) do + {events, meta} = Admin.list_events_paginated(params) + socket - |> assign(:page_title, "Events") + |> assign(:page_title, gettext("Events")) |> assign(:event, nil) + |> assign(:events, events) + |> assign(:meta, meta) + |> assign(:form, Phoenix.Component.to_form(meta)) + |> assign_new(:fields, fn -> + [ + name: [ + label: nil, + placeholder: gettext("Search events..."), + type: "text", + op: :ilike_or + ] + ] + end) end defp apply_action(socket, :new, _params) do @@ -56,120 +65,14 @@ defmodule ClaperWeb.AdminLive.EventLive do {:noreply, socket |> put_flash(:info, gettext("Event deleted successfully")) - |> assign(:events, list_events())} + |> push_patch(to: Flop.Phoenix.build_path(~p"/admin/events", socket.assigns.meta.flop))} end @impl true - def handle_event("search", %{"search" => search}, socket) do - events = search_events(search) - {:noreply, socket |> assign(:search, search) |> assign(:events, events)} - end + def handle_event("filter-events", unsigned_params, socket) do + flop = Flop.validate!(unsigned_params, for: Event, replace_invalid_params: true) + to = Flop.Phoenix.build_path(~p"/admin/events", flop) - @impl true - def handle_event("sort", %{"field" => field}, socket) do - field = String.to_existing_atom(field) - current_sort = socket.assigns.current_sort - - direction = - if current_sort.field == field && current_sort.order == :asc, do: :desc, else: :asc - - events = sort_events(socket.assigns.events, field, direction) - current_sort = %{field: field, order: direction} - - {:noreply, - socket - |> assign(:events, events) - |> assign(:current_sort, current_sort)} - end - - @impl true - def handle_info({:export_csv_requested, _params}, socket) do - filename = CSVExporter.generate_filename("events") - csv_content = CSVExporter.export_events_to_csv(socket.assigns.events) - - {:noreply, - socket - |> put_flash(:info, gettext("Events exported successfully")) - |> push_event("download_csv", %{filename: filename, content: csv_content})} - end - - @impl true - def handle_info({:table_action, action, event, _event_id}, socket) do - case action do - :view -> - {:noreply, push_navigate(socket, to: ~p"/admin/events/#{event}")} - - :edit -> - {:noreply, push_navigate(socket, to: ~p"/admin/events/#{event}/edit")} - - :delete -> - {:ok, _} = Claper.Events.delete_event(event) - - {:noreply, - socket - |> put_flash(:info, gettext("Event deleted successfully")) - |> assign(:events, list_events())} - end - end - - defp list_events do - Admin.list_all_events() - end - - defp search_events(search) when search == "", do: list_events() - - defp search_events(search) do - Admin.list_all_events(%{"search" => search}) - end - - defp sort_events(events, field, order) do - Enum.sort_by(events, &Map.get(&1, field), order) - end - - def sort_indicator(assigns) do - ~H""" - <%= if @current_sort.field == @field do %> - <%= if @current_sort.order == :asc do %> - - - - <% else %> - - - - <% end %> - <% else %> - - - - <% end %> - """ + {:noreply, push_patch(socket, to: to)} end end diff --git a/lib/claper_web/live/admin_live/event_live.html.heex b/lib/claper_web/live/admin_live/event_live.html.heex index b3f9e82..e84b045 100644 --- a/lib/claper_web/live/admin_live/event_live.html.heex +++ b/lib/claper_web/live/admin_live/event_live.html.heex @@ -1,249 +1,95 @@ <%= case @live_action do %> <% :index -> %>
-
-
-

{gettext("Events")}

-
- <%!-- <.link navigate={~p"/admin/events/new"} class="btn btn-primary btn-sm"> - - - - {gettext("New event")} - --%> -
-
-
+

{gettext("Events")}

-
-
- -
-
- -
+ <.form + for={@form} + class="flex flex-col sm:flex-row gap-3 sm:[&>div]:basis-[250px]" + phx-change="filter-events" + > + + + + + + {gettext("No events found")}

+ """, + table_attrs: [class: "table table-zebra w-full"], + symbol_asc: ~H""" + + """, + symbol_desc: ~H""" + + """ + ]} + > + <:col :let={event} label={gettext("Name")} field={:name}>{event.name} + <:col :let={event} label={gettext("Code")} field={:code}> + {event.code} + + <:col :let={event} label={gettext("Owner")}> + {if event.user, do: event.user.email, else: gettext("No owner")} + + <:col :let={event} label={gettext("Started At")} field={:started_at}> + {if event.started_at, + do: Calendar.strftime(event.started_at, "%Y-%m-%d %H:%M"), + else: gettext("Not started")} + + <:col :let={event} label={gettext("Expired At")} field={:expired_at}> + {if event.expired_at, + do: Calendar.strftime(event.expired_at, "%Y-%m-%d %H:%M"), + else: gettext("Not expired")} + + <:col :let={event} label={gettext("Audience Peak")} field={:audience_peak}> +
{event.audience_peak || 0}
+ + <:action :let={event} tbody_td_attrs={[class: "text-right"]}> +
+ <.link + navigate={~p"/admin/events/#{event}"} + class="btn btn-link btn-sm" + title={gettext("View event")} + > + + + <.link + navigate={~p"/admin/events/#{event}/edit"} + class="btn btn-link btn-sm text-primary" + title={gettext("Edit event")} + > + + + + +
- - -
-
-
- - - - - - - - - - - - - - <%= if Enum.empty?(@events) do %> - - - - <% else %> - <%= for event <- @events do %> - - - - - - - - - - <% end %> - <% end %> - -
- - - - - {gettext("Owner")} - - - - - - - - {gettext("Actions")} -
- {gettext("No events found")} -
- {event.name} - - {event.code} - - {if event.user, do: event.user.email, else: gettext("No owner")} - - {if event.started_at, - do: Calendar.strftime(event.started_at, "%Y-%m-%d %H:%M"), - else: gettext("Not started")} - - {if event.expired_at, - do: Calendar.strftime(event.expired_at, "%Y-%m-%d %H:%M"), - else: gettext("Not expired")} - -
- {event.audience_peak || 0} -
-
-
- <.link - navigate={~p"/admin/events/#{event}"} - class="btn btn-link btn-sm" - title={gettext("View event")} - > - - - - - - <.link - navigate={~p"/admin/events/#{event}/edit"} - class="btn btn-link btn-sm text-primary" - title={gettext("Edit event")} - > - - - - - - - - - -
-
-
-
-
-
+ + + +
+
<% :show -> %> diff --git a/lib/claper_web/live/admin_live/user_live.ex b/lib/claper_web/live/admin_live/user_live.ex index 8850d8c..c98680d 100644 --- a/lib/claper_web/live/admin_live/user_live.ex +++ b/lib/claper_web/live/admin_live/user_live.ex @@ -4,7 +4,6 @@ defmodule ClaperWeb.AdminLive.UserLive do alias Claper.Admin alias Claper.Accounts alias Claper.Accounts.User - alias ClaperWeb.Helpers.CSVExporter @impl true def mount(_params, session, socket) do @@ -12,12 +11,7 @@ defmodule ClaperWeb.AdminLive.UserLive do Gettext.put_locale(ClaperWeb.Gettext, locale) end - {:ok, - socket - |> assign(:page_title, gettext("Users")) - |> assign(:users, list_users()) - |> assign(:search, "") - |> assign(:current_sort, %{field: :na, order: :asc})} + {:ok, socket} end @impl true @@ -25,10 +19,25 @@ defmodule ClaperWeb.AdminLive.UserLive do {:noreply, apply_action(socket, socket.assigns.live_action, params)} end - defp apply_action(socket, :index, _params) do + defp apply_action(socket, :index, params) do + {users, meta} = Admin.list_users_paginated(params) + socket - |> assign(:page_title, "Users") + |> assign(:page_title, gettext("Users")) |> assign(:user, nil) + |> assign(:users, users) + |> assign(:meta, meta) + |> assign(:form, Phoenix.Component.to_form(meta)) + |> assign_new(:fields, fn -> + [ + email: [ + label: nil, + placeholder: gettext("Search users..."), + type: "text", + op: :ilike_or + ] + ] + end) end defp apply_action(socket, :new, _params) do @@ -57,142 +66,14 @@ defmodule ClaperWeb.AdminLive.UserLive do {:noreply, socket |> put_flash(:info, gettext("User deleted successfully")) - |> assign(:users, list_users())} + |> push_patch(to: Flop.Phoenix.build_path(~p"/admin/users", socket.assigns.meta.flop))} end @impl true - def handle_event("search", %{"search" => search}, socket) do - users = search_users(search) - {:noreply, socket |> assign(:search, search) |> assign(:users, users)} - end + def handle_event("filter-users", unsigned_params, socket) do + flop = Flop.validate!(unsigned_params, for: User, replace_invalid_params: true) + to = Flop.Phoenix.build_path(~p"/admin/users", flop) - @impl true - def handle_event("sort", %{"field" => field}, socket) do - field_atom = String.to_existing_atom(field) - current_sort = socket.assigns.current_sort - - new_direction = - if current_sort.field == field_atom do - if current_sort.order == :asc, do: :desc, else: :asc - else - :asc - end - - users = sort_users(socket.assigns.users, field_atom, new_direction) - current_sort = %{field: field_atom, order: new_direction} - - {:noreply, - socket - |> assign(:users, users) - |> assign(:current_sort, current_sort)} - end - - @impl true - def handle_info({:search_filter_changed, %{search: search, filters: _filters}}, socket) do - users = search_users(search) - {:noreply, socket |> assign(:search, search) |> assign(:users, users)} - end - - @impl true - def handle_info({:export_csv_requested, _params}, socket) do - filename = CSVExporter.generate_filename("users") - csv_content = CSVExporter.export_users_to_csv(socket.assigns.users) - - {:noreply, - socket - |> put_flash(:info, gettext("Users exported successfully")) - |> push_event("download_csv", %{filename: filename, content: csv_content})} - end - - @impl true - def handle_info({:table_sort_changed, sort_config}, socket) do - %{field: field, direction: direction} = sort_config - users = sort_users(socket.assigns.users, field, direction) - current_sort = %{field: field, order: direction} - - {:noreply, - socket - |> assign(:users, users) - |> assign(:current_sort, current_sort)} - end - - @impl true - def handle_info({:table_action, action, user, _user_id}, socket) do - case action do - :view -> - {:noreply, push_navigate(socket, to: ~p"/admin/users/#{user}")} - - :edit -> - {:noreply, push_navigate(socket, to: ~p"/admin/users/#{user}/edit")} - - :delete -> - {:ok, _} = Accounts.delete_user(user) - - {:noreply, - socket - |> put_flash(:info, gettext("User deleted successfully")) - |> assign(:users, list_users())} - end - end - - def list_users do - Admin.list_all_users() - end - - defp search_users(search) when search == "", do: list_users() - - defp search_users(search) do - Admin.list_all_users(%{"search" => search}) - end - - defp sort_users(users, field, order) do - Enum.sort_by(users, &Map.get(&1, field), order) - end - - def sort_indicator(assigns) do - ~H""" - <%= if @current_sort.field == @field do %> - <%= if @current_sort.order == :asc do %> - - - - <% else %> - - - - <% end %> - <% else %> - - - - <% end %> - """ + {:noreply, push_patch(socket, to: to)} end end diff --git a/lib/claper_web/live/admin_live/user_live.html.heex b/lib/claper_web/live/admin_live/user_live.html.heex index 48484c2..906a946 100644 --- a/lib/claper_web/live/admin_live/user_live.html.heex +++ b/lib/claper_web/live/admin_live/user_live.html.heex @@ -1,230 +1,89 @@ <%= case @live_action do %> <% :index -> %>
-
-
-

{gettext("Users")}

-
- <%!-- <.link navigate={~p"/admin/users/new"} class="btn btn-primary btn-sm"> - - - - {gettext("New user")} - --%> -
-
-
+

{gettext("Users")}

-
-
- -
-
- -
+ <.form + for={@form} + class="flex flex-col sm:flex-row gap-3 sm:[&>div]:basis-[250px]" + phx-change="filter-users" + > + + + + + + {gettext("No users found")}

+ """, + table_attrs: [class: "table table-zebra w-full"], + symbol_asc: ~H""" + + """, + symbol_desc: ~H""" + + """ + ]} + > + <:col :let={user} label={gettext("Email")} field={:email}>{user.email} + <:col :let={user} label={gettext("Role")} field={:role_id}> + {if user.role, do: user.role.name, else: gettext("No role")} + + <:col :let={user} label={gettext("Status")} field={:confirmed_at}> + <%= if user.confirmed_at do %> + {gettext("Confirmed")} + <% else %> + {gettext("Unconfirmed")} + <% end %> + + <:col :let={user} label={gettext("Created")} field={:inserted_at}> + {Calendar.strftime(user.inserted_at, "%Y-%m-%d %H:%M")} + + <:action :let={user} tbody_td_attrs={[class: "text-right"]}> +
+ <.link + navigate={~p"/admin/users/#{user}"} + class="btn btn-link btn-sm" + title={gettext("View user")} + > + + + <.link + navigate={~p"/admin/users/#{user}/edit"} + class="btn btn-link btn-sm text-primary" + title={gettext("Edit user")} + > + + + + +
- - -
-
-
- - - - - - - - - - - - <%= if Enum.empty?(@users) do %> - - - - <% else %> - <%= for user <- @users do %> - - - - - - - - <% end %> - <% end %> - -
- - - - - - - - - {gettext("Actions")} -
- {gettext("No users found")} -
- {user.email} - - {if user.role, do: user.role.name, else: gettext("No role")} - - <%= if user.confirmed_at do %> - - {gettext("Confirmed")} - - <% else %> - - {gettext("Unconfirmed")} - - <% end %> - - {Calendar.strftime(user.inserted_at, "%Y-%m-%d %H:%M")} - -
- <.link - navigate={~p"/admin/users/#{user}"} - class="btn btn-link btn-sm" - title={gettext("View user")} - > - - - - - - <.link - navigate={~p"/admin/users/#{user}/edit"} - class="btn btn-link btn-sm text-primary" - title={gettext("Edit user")} - > - - - - - - - - - -
-
-
-
-
-
+ + + +
+
<% :show -> %> diff --git a/lib/claper_web/live/event_live/event_card_component.ex b/lib/claper_web/live/event_live/event_card_component.ex index b4dcf36..d03c9d4 100644 --- a/lib/claper_web/live/event_live/event_card_component.ex +++ b/lib/claper_web/live/event_live/event_card_component.ex @@ -22,10 +22,14 @@ defmodule ClaperWeb.EventLive.EventCardComponent do Presentations.get_first_slide_url(event.presentation_file) end + defp dom_id(component_id, suffix) do + "#{component_id}-#{suffix}" + end + defp render_grid_card(assigns) do ~H"""
{gettext("Processing...")}
- +
<%= if Event.started?(@event) && !Event.finished?(@event) do %> @@ -69,7 +73,7 @@ defmodule ClaperWeb.EventLive.EventCardComponent do
<% end %>
- +
@@ -88,7 +92,7 @@ defmodule ClaperWeb.EventLive.EventCardComponent do LTI
- +
- +
- +
- +
{gettext("Error when processing the file")}
- +
@@ -277,7 +281,7 @@ defmodule ClaperWeb.EventLive.EventCardComponent do defp render_list_card(assigns) do ~H""" -
+
@@ -297,7 +301,7 @@ defmodule ClaperWeb.EventLive.EventCardComponent do Loading
- +
@@ -341,19 +345,19 @@ defmodule ClaperWeb.EventLive.EventCardComponent do # {@event.code} {gettext("Starting on")} - + {gettext("Finished on")}
- +
<%= if @event.presentation_file.status == "done" && !Event.finished?(@event) do %> @@ -491,12 +495,12 @@ defmodule ClaperWeb.EventLive.EventCardComponent do {gettext("View report")} <% end %> - +