mirror of
https://codeberg.org/JasterV/spazio-solazzo.git
synced 2026-04-26 18:20:03 +00:00
251 lines
7.1 KiB
Elixir
251 lines
7.1 KiB
Elixir
defmodule SpazioSolazzoWeb.Admin.BookingManagementLive do
|
|
@moduledoc """
|
|
Admin booking management tool for reviewing and managing all booking requests.
|
|
Refactored to use URL as the Single Source of Truth.
|
|
"""
|
|
|
|
use SpazioSolazzoWeb, :live_view
|
|
|
|
import SpazioSolazzoWeb.AdminBookingManagementComponents
|
|
alias SpazioSolazzo.BookingSystem
|
|
|
|
@pending_limit 10
|
|
@history_limit 10
|
|
|
|
def mount(_params, _session, socket) do
|
|
{:ok, spaces} = Ash.read(SpazioSolazzo.BookingSystem.Space)
|
|
|
|
if connected?(socket) do
|
|
Phoenix.PubSub.subscribe(SpazioSolazzo.PubSub, "booking:created")
|
|
Phoenix.PubSub.subscribe(SpazioSolazzo.PubSub, "booking:approved")
|
|
Phoenix.PubSub.subscribe(SpazioSolazzo.PubSub, "booking:cancelled")
|
|
Phoenix.PubSub.subscribe(SpazioSolazzo.PubSub, "booking:rejected")
|
|
end
|
|
|
|
{:ok,
|
|
assign(socket,
|
|
spaces: spaces,
|
|
expanded_booking_ids: MapSet.new(),
|
|
show_reject_modal: false,
|
|
rejecting_booking_id: nil,
|
|
rejection_reason: ""
|
|
)}
|
|
end
|
|
|
|
def handle_params(params, _uri, socket) do
|
|
# Destructure params with defaults. This ensures all keys exist.
|
|
%{
|
|
"space" => space_slug,
|
|
"email" => email,
|
|
"date" => date_str,
|
|
"pending_page" => pending_str,
|
|
"history_page" => history_str
|
|
} =
|
|
Map.merge(
|
|
%{
|
|
"space" => nil,
|
|
"email" => "",
|
|
"date" => nil,
|
|
"pending_page" => "1",
|
|
"history_page" => "1"
|
|
},
|
|
params
|
|
)
|
|
|
|
socket =
|
|
socket
|
|
|> assign(
|
|
filter_space: parse_string(space_slug),
|
|
filter_email: email,
|
|
filter_date: parse_date(date_str),
|
|
pending_page_number: parse_page(pending_str),
|
|
history_page_number: parse_page(history_str)
|
|
)
|
|
|> fetch_bookings()
|
|
|
|
{:noreply, socket}
|
|
end
|
|
|
|
def handle_event(
|
|
"filter_bookings",
|
|
%{"date" => date, "space" => space, "email" => email},
|
|
socket
|
|
) do
|
|
params = %{
|
|
"date" => date,
|
|
"space" => space,
|
|
"email" => email,
|
|
# Reset pagination to page 1 when filtering
|
|
"pending_page" => "1",
|
|
"history_page" => "1"
|
|
}
|
|
|
|
{:noreply, push_patch(socket, to: build_filter_path(socket, params), replace: true)}
|
|
end
|
|
|
|
def handle_event("clear_filters", _, socket) do
|
|
{:noreply, push_patch(socket, to: ~p"/admin/bookings", replace: true)}
|
|
end
|
|
|
|
def handle_event("pending_page_change", %{"page" => page}, socket) do
|
|
{:noreply, push_patch(socket, to: build_filter_path(socket, %{"pending_page" => page}))}
|
|
end
|
|
|
|
def handle_event("history_page_change", %{"page" => page}, socket) do
|
|
{:noreply, push_patch(socket, to: build_filter_path(socket, %{"history_page" => page}))}
|
|
end
|
|
|
|
def handle_event("toggle_expand", %{"booking_id" => booking_id}, socket) do
|
|
expanded =
|
|
if MapSet.member?(socket.assigns.expanded_booking_ids, booking_id) do
|
|
MapSet.delete(socket.assigns.expanded_booking_ids, booking_id)
|
|
else
|
|
MapSet.put(socket.assigns.expanded_booking_ids, booking_id)
|
|
end
|
|
|
|
{:noreply, assign(socket, expanded_booking_ids: expanded)}
|
|
end
|
|
|
|
def handle_event("approve_booking", %{"booking_id" => booking_id}, socket) do
|
|
case Ash.get(BookingSystem.Booking, booking_id) do
|
|
{:ok, booking} ->
|
|
BookingSystem.approve_booking(booking)
|
|
{:noreply, socket}
|
|
|
|
{:error, _} ->
|
|
{:noreply, put_flash(socket, :error, "Booking not found")}
|
|
end
|
|
end
|
|
|
|
def handle_event("show_reject_modal", %{"booking_id" => booking_id}, socket) do
|
|
{:noreply,
|
|
assign(socket,
|
|
show_reject_modal: true,
|
|
rejecting_booking_id: booking_id,
|
|
rejection_reason: ""
|
|
)}
|
|
end
|
|
|
|
def handle_event("hide_reject_modal", _, socket) do
|
|
{:noreply, assign(socket, show_reject_modal: false, rejecting_booking_id: nil)}
|
|
end
|
|
|
|
def handle_event("stop_propagation", _, socket), do: {:noreply, socket}
|
|
|
|
def handle_event("update_rejection_reason", %{"reason" => reason}, socket) do
|
|
{:noreply, assign(socket, rejection_reason: reason)}
|
|
end
|
|
|
|
def handle_event("confirm_reject", _, socket) do
|
|
%{rejecting_booking_id: id, rejection_reason: reason} = socket.assigns
|
|
|
|
if String.trim(reason) == "" do
|
|
{:noreply, put_flash(socket, :error, "Please provide a rejection reason")}
|
|
else
|
|
case Ash.get(BookingSystem.Booking, id) do
|
|
{:ok, booking} ->
|
|
BookingSystem.reject_booking(booking, reason)
|
|
|
|
socket =
|
|
socket
|
|
|> assign(show_reject_modal: false, rejecting_booking_id: nil)
|
|
|> put_flash(:info, "Booking rejected")
|
|
|
|
{:noreply, socket}
|
|
|
|
{:error, _} ->
|
|
{:noreply, put_flash(socket, :error, "Booking not found")}
|
|
end
|
|
end
|
|
end
|
|
|
|
def handle_info(%{topic: "booking:" <> _}, socket) do
|
|
{:noreply, fetch_bookings(socket)}
|
|
end
|
|
|
|
def handle_info(_msg, socket), do: {:noreply, socket}
|
|
|
|
defp fetch_bookings(socket) do
|
|
%{
|
|
filter_space: space_slug,
|
|
filter_email: email,
|
|
filter_date: date,
|
|
pending_page_number: pending_page,
|
|
history_page_number: history_page,
|
|
spaces: spaces
|
|
} = socket.assigns
|
|
|
|
space_id =
|
|
spaces
|
|
|> Enum.find(%{}, fn s -> s.slug == space_slug end)
|
|
|> Map.get(:id, nil)
|
|
|
|
query_email = parse_string(email)
|
|
pending_offset = (pending_page - 1) * @pending_limit
|
|
history_offset = (history_page - 1) * @history_limit
|
|
|
|
{:ok, pending_data} =
|
|
BookingSystem.read_pending_bookings(space_id, query_email, date,
|
|
page: [limit: @pending_limit, offset: pending_offset, count: true],
|
|
load: [:space, :user]
|
|
)
|
|
|
|
{:ok, history_data} =
|
|
BookingSystem.read_booking_history(space_id, query_email, date,
|
|
page: [limit: @history_limit, offset: history_offset, count: true],
|
|
load: [:space, :user]
|
|
)
|
|
|
|
assign(socket, pending_page: pending_data, history_page: history_data)
|
|
end
|
|
|
|
defp build_filter_path(socket, overrides) do
|
|
current = %{
|
|
"space" => socket.assigns.filter_space,
|
|
"email" => socket.assigns.filter_email,
|
|
"date" => socket.assigns.filter_date,
|
|
"pending_page" => socket.assigns.pending_page_number,
|
|
"history_page" => socket.assigns.history_page_number
|
|
}
|
|
|
|
# Merge overrides -> Filter empty values -> Encode
|
|
params =
|
|
current
|
|
|> Map.merge(overrides)
|
|
|> Enum.reduce(%{}, fn
|
|
{_k, nil}, acc -> acc
|
|
{_k, ""}, acc -> acc
|
|
{"date", %Date{} = d}, acc -> Map.put(acc, "date", Date.to_iso8601(d))
|
|
{k, v}, acc -> Map.put(acc, k, v)
|
|
end)
|
|
|
|
~p"/admin/bookings"
|
|
|> URI.parse()
|
|
|> URI.append_query(URI.encode_query(params))
|
|
|> URI.to_string()
|
|
end
|
|
|
|
defp parse_page(nil), do: 1
|
|
defp parse_page(""), do: 1
|
|
|
|
defp parse_page(value) do
|
|
case Integer.parse(value) do
|
|
{n, _} when n > 0 -> n
|
|
_ -> 1
|
|
end
|
|
end
|
|
|
|
defp parse_date(nil), do: nil
|
|
defp parse_date(""), do: nil
|
|
|
|
defp parse_date(value) do
|
|
case Date.from_iso8601(value) do
|
|
{:ok, date} -> date
|
|
_ -> nil
|
|
end
|
|
end
|
|
|
|
defp parse_string(nil), do: nil
|
|
defp parse_string(""), do: nil
|
|
defp parse_string(value), do: value
|
|
end
|