diff --git a/lib/haj/policy/policy.ex b/lib/haj/policy/policy.ex index f7bba8e..90fa528 100644 --- a/lib/haj/policy/policy.ex +++ b/lib/haj/policy/policy.ex @@ -91,5 +91,10 @@ defmodule Haj.Policy do allow role: :admin allow current_group_member: :chefsgruppen end + + action :approve do + allow :is_chef + allow role: :admin + end end end diff --git a/lib/haj_web/components/components/steps.html.heex b/lib/haj_web/components/components/steps.html.heex index bd1e50f..b79b7eb 100644 --- a/lib/haj_web/components/components/steps.html.heex +++ b/lib/haj_web/components/components/steps.html.heex @@ -3,7 +3,7 @@ <%= "Steg #{Enum.find_index(@step, fn step -> step.status == :current end) + 1} av #{length(@step)}" %>

    -
  1. +
  2. <.link :if={step.status == :complete} navigate={step.to} diff --git a/lib/haj_web/components/core_components.ex b/lib/haj_web/components/core_components.ex index e1acaca..737af0e 100644 --- a/lib/haj_web/components/core_components.ex +++ b/lib/haj_web/components/core_components.ex @@ -485,6 +485,7 @@ defmodule HajWeb.CoreComponents do slot :col, required: true do attr :label, :string + attr :class, :string end slot :action, doc: "the slot for showing user actions in the last table column" @@ -497,10 +498,12 @@ defmodule HajWeb.CoreComponents do ~H"""
    - +
    - + @@ -513,7 +516,7 @@ defmodule HajWeb.CoreComponents do
    <%= col[:label] %> + <%= col[:label] %> + <%= gettext("Actions") %>
    @@ -580,7 +583,7 @@ defmodule HajWeb.CoreComponents do ~H"""
    - +
    diff --git a/lib/haj_web/components/layouts.ex b/lib/haj_web/components/layouts.ex index bd4521d..1193b87 100644 --- a/lib/haj_web/components/layouts.ex +++ b/lib/haj_web/components/layouts.ex @@ -76,6 +76,14 @@ defmodule HajWeb.Layouts do /> + <.nav_link + :if={Policy.authorize?(:applications_read, @current_user)} + navigate={~p"/applications"} + icon_name={:user_plus} + title="Ansökningar" + active={@active_tab == :applications} + /> + <.nav_link navigate={~p"/songs"} icon_name={:musical_note} diff --git a/lib/haj_web/components/layouts/live.html.heex b/lib/haj_web/components/layouts/live.html.heex index efefe20..a3bf706 100644 --- a/lib/haj_web/components/layouts/live.html.heex +++ b/lib/haj_web/components/layouts/live.html.heex @@ -3,7 +3,7 @@
    @@ -19,7 +19,7 @@ <.icon name={:chevron_left} class="h-full w-full text-white" />
    -
    -
    +
    token}, socket) do - current_spex = Spex.current_spex() |> Haj.Repo.preload(show_groups: [group: []]) - applications = Applications.list_applications_for_show(current_spex.id) - - socket = - socket - |> assign_new(:current_user, fn -> - Haj.Accounts.get_user_by_session_token(token) |> Haj.Spex.preload_user_groups() - end) - |> assign(:title, "Ansökningar #{current_spex.year.year}") - |> assign(:show, current_spex) - |> assign(:applications, applications) - |> assign(active_tab: nil, expanded_tab: nil) - - {:ok, socket} - end - - def handle_event("filter", %{"filter" => %{"show_group" => show_group_id}}, socket) do - applications = - case show_group_id do - "" -> Applications.list_applications_for_show(socket.assigns.show.id) - id -> Applications.get_applications_for_show_group(id) - end - - {:noreply, assign(socket, applications: applications)} - end - - def render(assigns) do - ~H""" -
    -
    Filtrera
    - - <.form :let={f} for={%{}} as={:filter} phx-change="filter" class="w-full md:w-auto"> - <%= select(f, :show_group, group_options(@show.show_groups), - class: "h-full w-full", - prompt: "Alla grupper" - ) %> - -
    - -
    -
    <%= col[:label] %>
    - - - <%= for col <- ["Namn", "Email", "Telefon", "Klass", "Grupper"] do %> - - <% end %> - - - - <%= if @applications == [] do %> - - <% else %> - <%= for {application, i} <- Enum.with_index(@applications) do %> - - - - - - - - - - - - - <% end %> - <% end %> - -
    - <%= col %> -
    Här var det tomt.
    -
    - - <%= "#{application.user.first_name} #{application.user.last_name}" %> -
    -
    - <%= application.user.email %> - - <%= application.user.phone %> - - <%= application.user.class %> - - <%= all_groups(application) %> -
    -
    Tid för ansökan: <%= application.inserted_at %>
    -
    -
    -

    Övrigt

    - <%= if application.other == nil do %> - Inget svar - <%= else %> -

    <%= application.other %>

    - <%= end %> -
    -
    -
    -

    Eventuell rangordning

    - <%= if application.ranking == nil do %> - Inget svar - <%= else %> -

    <%= application.ranking %>

    - <%= end %> -
    - <%= for asg <- application.application_show_groups do %> - <%= if asg.show_group.application_extra_question do %> -
    -
    -

    <%= asg.show_group.application_extra_question %>

    - <%= if asg.special_text == nil do %> - Inget svar - <%= else %> -

    <%= asg.special_text %>

    - <%= end %> -
    - <% end %> - <% end %> -
    -
    - """ - end - - defp group_options(show_group) do - show_group - # |> Enum.filter(fn %{application_open: open} -> open end) # commented out to only allow SpexM to be applicable - |> Enum.map(fn %{id: id, group: g} -> [key: g.name, value: id] end) - end - - defp all_groups(application) do - Enum.map(application.application_show_groups, fn %{show_group: %{group: group}} -> - group.name - end) - |> Enum.join(", ") - end -end diff --git a/lib/haj_web/live/applications_live/approve_component.ex b/lib/haj_web/live/applications_live/approve_component.ex new file mode 100644 index 0000000..d33b045 --- /dev/null +++ b/lib/haj_web/live/applications_live/approve_component.ex @@ -0,0 +1,91 @@ +defmodule HajWeb.ApplicationsLive.ApproveComponent do + use HajWeb, :live_component + + alias Haj.Policy + alias Haj.Spex + + def update(%{application: app} = assigns, socket) do + memberships = + Haj.Spex.get_show_groups_for_user(app.user_id) + |> Enum.map(fn %{id: id} -> id end) + + {:ok, socket |> assign(assigns) |> assign(memberships: memberships)} + end + + def handle_event("approve", %{"show_group" => show_group_id}, socket) do + show_group = Haj.Spex.get_show_group!(show_group_id) + application = socket.assigns.application + + if Policy.authorize?( + :applications_approve, + socket.assigns.current_user, + show_group + ) do + if Spex.is_member_of_show_group?(application.user.id, show_group_id) do + push_flash( + :error, + "#{application.user.full_name} är redan medlem i #{show_group.group.name}." + ) + else + case Haj.Spex.create_group_membership(%{ + user_id: application.user.id, + show_group_id: show_group_id, + role: :gruppis + }) do + {:ok, _} -> + push_flash( + :info, + "#{application.user.full_name} antogs till #{show_group.group.name}." + ) + + {:error, _} -> + push_flash(:error, "Något gick fel.") + end + end + else + push_flash(:error, "Du har inte rättigheter att anta till denna grupp.") + end + + {:noreply, socket} + end + + def render(assigns) do + ~H""" +
    + <.header> + Antag <%= @application.user.full_name %> + <:subtitle>Antag personen till grupper. + + + <.form + :let={_f} + for={%{}} + as={:group} + phx-submit="approve" + class="mt-2 flex flex-col gap-2" + phx-target={@myself} + > + <.input + type="select" + name="show_group" + prompt="Välj grupp" + class="flex-grow" + value="" + options={group_options(@application.application_show_groups, @memberships)} + /> + +
    + <.button type="submit"> + Antag + +
    + +
    + """ + end + + defp group_options(show_groups, memberships) do + Enum.reject(show_groups, fn asg -> asg.show_group_id in memberships end) + |> Enum.map(fn asg -> [key: asg.show_group.group.name, value: asg.show_group.id] end) + end +end diff --git a/lib/haj_web/live/applications_live/index.ex b/lib/haj_web/live/applications_live/index.ex new file mode 100644 index 0000000..2f62973 --- /dev/null +++ b/lib/haj_web/live/applications_live/index.ex @@ -0,0 +1,69 @@ +defmodule HajWeb.ApplicationsLive.Index do + use HajWeb, :live_view + + alias Haj.Spex + alias Haj.Applications + + on_mount {HajWeb.UserAuth, {:authorize, :applications_read}} + + def mount(_params, _session, socket) do + current_spex = Spex.current_spex() |> Haj.Repo.preload(show_groups: [group: []]) + applications = Applications.list_applications_for_show(current_spex.id) + + most_popular_group = + applications + |> Enum.flat_map(fn app -> app.application_show_groups end) + |> Enum.group_by(fn asg -> asg.show_group_id end) + |> Enum.map(fn {_, asgs} -> {hd(asgs).show_group.group, length(asgs)} end) + |> Enum.sort_by(fn {_, asgs} -> asgs end, :desc) + |> Enum.take(1) + |> hd() + |> elem(0) + + stats = %{ + apps: length(applications), + group_apps: + Enum.reduce(applications, 0, fn app, acc -> acc + length(app.application_show_groups) end), + most_popular_group: most_popular_group.name + } + + {:ok, + socket + |> assign(:page_title, "Ansökningar") + |> assign(:show, current_spex) + |> assign(:applications, applications) + |> assign(:stats, stats) + |> assign(:selected, "")} + end + + def handle_params(%{"show_group_id" => id}, _uri, socket) do + applications = Applications.get_applications_for_show_group(id) + {:noreply, assign(socket, applications: applications, selected: id)} + end + + def handle_params(_params, _uri, socket), do: {:noreply, socket} + + def handle_event("filter", %{"show_group" => show_group_id}, socket) do + case show_group_id do + "" -> + apps = Applications.list_applications_for_show(socket.assigns.show.id) + + {:noreply, + assign(socket, applications: apps) + |> push_patch(to: ~p"/applications")} + + id -> + apps = Applications.get_applications_for_show_group(id) + + {:noreply, + assign(socket, applicatons: apps) + |> push_patch(to: ~p"/applications?show_group_id=#{show_group_id}")} + end + end + + defp group_options(show_group) do + show_group + # |> Enum.filter(fn %{application_open: open} -> open end) # commented out to only allow SpexM to be applicable + |> Enum.map(fn %{id: id, group: g} -> [key: g.name, value: id] end) + end +end diff --git a/lib/haj_web/live/applications_live/index.html.heex b/lib/haj_web/live/applications_live/index.html.heex new file mode 100644 index 0000000..2904990 --- /dev/null +++ b/lib/haj_web/live/applications_live/index.html.heex @@ -0,0 +1,109 @@ +
    +
    + Ansökningar +
    + +
    + + + <.form :let={_f} for={%{}} as={:filter} phx-change="filter" class="mt-2 flex gap-2"> + <.input + type="select" + name="show_group" + prompt="Alla grupper" + class="flex-grow" + options={group_options(@show.show_groups)} + value={@selected} + /> + + <.link + href={~p"/applications/export/csv"} + class="mt-1 flex flex-row items-center rounded-md border border-gray-300 bg-white px-3 py-2 shadow-sm hover:bg-gray-50" + > + <.icon name={:arrow_down_on_square_stack} mini class="h-5 w-5" /> + Exportera csv + + + + <.table + small + id="applicaiton_table" + rows={@applications} + row_click={fn app -> JS.navigate(~p"/applications/#{app}") end} + > + <:col :let={app} label="Namn"> + <%= app.user.full_name %> + + + <:col :let={app} label="Email" class="hidden sm:table-cell"> + <%= app.user.email %> + + <:col :let={app} label="Telefon" class="hidden xs:table-cell"> + <%= app.user.phone %> + + <:col :let={app} label="Klass" class=""> + <%= app.user.class %> + + <:col :let={app} label="Grupper" class="hidden md:table-cell"> +
    + <.link + :for={group <- app.application_show_groups} + navigate={~p"/group/#{group.show_group_id}"} + > +
    + <%= group.show_group.group.name %> +
    + +
    + + + + <%!-- + + Mer statistik + + + --%> +
    +
    diff --git a/lib/haj_web/live/applications_live/show.ex b/lib/haj_web/live/applications_live/show.ex new file mode 100644 index 0000000..c10d437 --- /dev/null +++ b/lib/haj_web/live/applications_live/show.ex @@ -0,0 +1,44 @@ +defmodule HajWeb.ApplicationsLive.Show do + use HajWeb, :live_view + + alias Haj.Applications + alias Haj.Repo + + on_mount {HajWeb.UserAuth, {:authorize, :applications_read}} + + def mount(%{"id" => id}, _session, socket) do + application = + Applications.get_application!(id) + |> Repo.preload(application_show_groups: [show_group: [:group]], user: []) + + show = Haj.Spex.current_spex() + + {:ok, assign(socket, application: application, show: show)} + end + + def handle_params(params, _url, socket) do + {:noreply, apply_action(socket, socket.assigns.live_action, params)} + end + + defp apply_action(socket, :show, _params), do: assign(socket, :page_title, "Ansökan") + defp apply_action(socket, :approve, _params), do: assign(socket, :page_title, "Antag") + + attr :large, :boolean, default: false + attr :name, :string + slot :inner_block + + defp field(assigns) do + ~H""" +
    +
    <%= @name %>
    +
    + <%= render_slot(@inner_block) %> +
    +
    + """ + end +end diff --git a/lib/haj_web/live/applications_live/show.html.heex b/lib/haj_web/live/applications_live/show.html.heex new file mode 100644 index 0000000..10be409 --- /dev/null +++ b/lib/haj_web/live/applications_live/show.html.heex @@ -0,0 +1,87 @@ +
    +
    +
    +

    Ansökan

    +

    + Ansökan till METAspexet <%= @show.year.year %> +

    +
    + <.link navigate={~p"/applications/#{@application}/confirm"}> + <.button> + Antag + + +
    +
    +
    + <.field name="Namn"> + <%= @application.user.full_name %> + + <.field name="Klass"> + <%= @application.user.class %> + + <.field name="Email"> + <%= @application.user.email %> + + <.field name="Telefon"> + <%= @application.user.phone %> + + <.field name="Tid för ansökan"> + <%= @application.updated_at %> + + + <.field name="Grupper"> +
    + <.link + :for={group <- @application.application_show_groups} + navigate={~p"/group/#{group.show_group_id}"} + > +
    + <%= group.show_group.group.name %> +
    + +
    + + + <.field :if={length(@application.application_show_groups) > 1} large name="Rangordning"> + Inget svar + <%= @application.ranking %> + + + <.field large name="Övrigt"> + Inget svar + <%= @application.other %> + + + <.field + :for={asg <- @application.application_show_groups} + :if={asg.show_group.application_extra_question} + large + name={asg.show_group.application_extra_question} + > + Inget svar + <%= asg.special_text %> + +
    +
    +
    + +<.modal + :if={@live_action in [:approve]} + id="app-confirm-modal" + show + on_cancel={JS.navigate(~p"/applications/#{@application}")} +> + <.live_component + module={HajWeb.ApplicationsLive.ApproveComponent} + current_user={@current_user} + id={@application.id} + application={@application} + action={@live_action} + navigate={~p"/applications/#{@application}"} + /> + diff --git a/lib/haj_web/live/group_live/admin.ex b/lib/haj_web/live/group_live/admin.ex index a5cb865..94e676e 100644 --- a/lib/haj_web/live/group_live/admin.ex +++ b/lib/haj_web/live/group_live/admin.ex @@ -32,7 +32,7 @@ defmodule HajWeb.GroupLive.Admin do {:ok, socket |> put_flash(:error, "Du har inte behörighet att redigera denna grupp") - |> redirect(to: ~p"/live/group/#{show_group}")} + |> redirect(to: ~p"/group/#{show_group}")} end end diff --git a/lib/haj_web/live/members_live.ex b/lib/haj_web/live/members_live.ex index 75b9a62..576e4f9 100644 --- a/lib/haj_web/live/members_live.ex +++ b/lib/haj_web/live/members_live.ex @@ -97,8 +97,4 @@ defmodule HajWeb.MembersLive do """ end - - @colors ~w"#8dd3c7 #ffffb3 #bebada #fb8072 #80b1d3 #fdb462 #b3de69 #fccde5 #d9d9d9 #bc80bd #ccebc5 #ffed6f" - - defp get_color(:bg, index), do: Enum.at(@colors, rem(index - 1, 12), "#4e79a7") end diff --git a/lib/haj_web/live/nav_live.ex b/lib/haj_web/live/nav_live.ex index 58ca360..3006c64 100644 --- a/lib/haj_web/live/nav_live.ex +++ b/lib/haj_web/live/nav_live.ex @@ -58,6 +58,7 @@ defmodule HajWeb.Nav do tab SongLive.Index, :songs tab SongLive.Show, :songs tab ShowLive.Index, :shows + tab ApplicationsLive.Index, :applications tab SettingsLive.Index, :settings tab SettingsLive.Show.Index, {:setting, :shows} diff --git a/lib/haj_web/live/responsibility_live/history.ex b/lib/haj_web/live/responsibility_live/history.ex index b990d6d..956c0e9 100644 --- a/lib/haj_web/live/responsibility_live/history.ex +++ b/lib/haj_web/live/responsibility_live/history.ex @@ -13,7 +13,12 @@ defmodule HajWeb.ResponsibilityLive.History do {current, prev} = Enum.split_with(responsibilities, fn %{show_id: id} -> id == current_show.id end) - {:ok, assign(socket, current_responsibilities: current, prev_responsibilities: prev)} + {:ok, + assign(socket, + page_title: "Dina ansvar", + current_responsibilities: current, + prev_responsibilities: prev + )} end attr :navigate, :any, required: true diff --git a/lib/haj_web/live/responsibility_live/index.ex b/lib/haj_web/live/responsibility_live/index.ex index 9ae0ed9..68ba4d2 100644 --- a/lib/haj_web/live/responsibility_live/index.ex +++ b/lib/haj_web/live/responsibility_live/index.ex @@ -18,19 +18,19 @@ defmodule HajWeb.ResponsibilityLive.Index do defp apply_action(socket, :edit, %{"id" => id}) do socket - |> assign(:page_title, "Edit Responsibility") + |> assign(:page_title, "Redigera ansvar") |> assign(:responsibility, Responsibilities.get_responsibility!(id)) end defp apply_action(socket, :new, _params) do socket - |> assign(:page_title, "New Responsibility") + |> assign(:page_title, "Nytt ansvar") |> assign(:responsibility, %Responsibility{}) end defp apply_action(socket, :index, _params) do socket - |> assign(:page_title, "Listing Responsibilities") + |> assign(:page_title, "Ansvar") |> assign(:responsibility, nil) end diff --git a/lib/haj_web/live/responsibility_live/show.ex b/lib/haj_web/live/responsibility_live/show.ex index 563a170..3a1e7d8 100644 --- a/lib/haj_web/live/responsibility_live/show.ex +++ b/lib/haj_web/live/responsibility_live/show.ex @@ -16,16 +16,16 @@ defmodule HajWeb.ResponsibilityLive.Show do def handle_params(%{"id" => id} = params, _, socket) do responsibility = Responsibilities.get_responsibility!(id) - socket = - socket - |> assign(:responsibility, responsibility) - - {:noreply, socket |> apply_action(socket.assigns.live_action, params)} + {:noreply, + socket + |> assign(:responsibility, responsibility) + |> apply_action(socket.assigns.live_action, params)} end defp apply_action(socket, :show, %{"id" => _id}) do - socket - |> assign(page_title: "Ansvar") + %{responsibility: responsibility} = socket.assigns + + assign(socket, page_title: responsibility.name) end defp apply_action(socket, :edit, %{"id" => _id}) do @@ -85,7 +85,7 @@ defmodule HajWeb.ResponsibilityLive.Show do authorized = Policy.authorize?(:responsibility_comment_create, user, responsibility) socket - |> assign(page_title: "Testamenten") + |> assign(page_title: "#{responsibility.name} · Testamenten") |> assign(show: show) |> assign(comments: Responsibilities.get_comments_for_show(responsibility, show.id)) |> assign(authorized: authorized) @@ -97,8 +97,10 @@ defmodule HajWeb.ResponsibilityLive.Show do end defp apply_action(socket, :history, _params) do + %{responsibility: responsibility} = socket.assigns + socket - |> assign(page_title: "Historik") + |> assign(page_title: "#{responsibility.name} · Historik") |> assign( :responsible_users, Responsibilities.get_users_for_responsibility_grouped(socket.assigns.responsibility.id) diff --git a/lib/haj_web/live/show_live/show.ex b/lib/haj_web/live/show_live/show.ex index 5fc69aa..0e8f782 100644 --- a/lib/haj_web/live/show_live/show.ex +++ b/lib/haj_web/live/show_live/show.ex @@ -42,8 +42,4 @@ defmodule HajWeb.ShowLive.Show do {:noreply, assign(socket, members: members, query: query, group: group)} end - - @colors ~w"#8dd3c7 #ffffb3 #bebada #fb8072 #80b1d3 #fdb462 #b3de69 #fccde5 #d9d9d9 #bc80bd #ccebc5 #ffed6f" - - defp get_color(:bg, index), do: Enum.at(@colors, rem(index - 1, 12), "#4e79a7") end diff --git a/lib/haj_web/live/song_live/index.ex b/lib/haj_web/live/song_live/index.ex index e50cb1a..06a9499 100644 --- a/lib/haj_web/live/song_live/index.ex +++ b/lib/haj_web/live/song_live/index.ex @@ -14,7 +14,8 @@ defmodule HajWeb.SongLive.Index do [key: "#{year.year}: #{title}", value: id] end) - {:ok, assign(socket, songs: songs, show: show, show_options: show_options)} + {:ok, + assign(socket, page_title: "Sånger", songs: songs, show: show, show_options: show_options)} end def handle_event("select_show", %{"show" => show_id}, socket) do diff --git a/lib/haj_web/live/song_live/show.ex b/lib/haj_web/live/song_live/show.ex index 68a6745..7fe5c4b 100644 --- a/lib/haj_web/live/song_live/show.ex +++ b/lib/haj_web/live/song_live/show.ex @@ -15,7 +15,14 @@ defmodule HajWeb.SongLive.Show do lyrics = parse_lyrics(song.text) - {:noreply, assign(socket, song: song, lyrics: lyrics, player: false, loaded: false)} + {:noreply, + assign(socket, + page_title: song.name, + song: song, + lyrics: lyrics, + player: false, + loaded: false + )} end def handle_event("play_pause", _params, socket) do diff --git a/lib/haj_web/router.ex b/lib/haj_web/router.ex index efa39f1..154f290 100644 --- a/lib/haj_web/router.ex +++ b/lib/haj_web/router.ex @@ -87,6 +87,11 @@ defmodule HajWeb.Router do live "/responsibilities/:id/history", ResponsibilityLive.Show, :history live "/responsibilities/:id/show/edit", ResponsibilityLive.Show, :edit + ## Applications + live "/applications", ApplicationsLive.Index, :index + live "/applications/:id", ApplicationsLive.Show, :show + live "/applications/:id/confirm", ApplicationsLive.Show, :approve + ## Songs live "/songs", SongLive.Index, :index live "/songs/:id", SongLive.Show, :show @@ -175,8 +180,8 @@ defmodule HajWeb.Router do scope "/" do pipe_through :require_applications_read - get "/applications", ApplicationController, :index - get "/applications/export", ApplicationController, :export + # get "/applications", ApplicationController, :index + get "/applications/export/csv", ApplicationController, :export end end diff --git a/lib/haj_web/templates/application/index.html.heex b/lib/haj_web/templates/application/index.html.heex deleted file mode 100644 index 9e6f222..0000000 --- a/lib/haj_web/templates/application/index.html.heex +++ /dev/null @@ -1,11 +0,0 @@ -
    -
    -
    Ansökningar
    -
    - <%= link("Exportera som csv", to: Routes.application_path(@conn, :export)) %> -
    -
    -
    - <%= live_render(@conn, HajWeb.ApplicationsLive) %> -
    -