defmodule ToweropsWeb.ReportsLive do @moduledoc """ Scheduled Reports management page. Lists existing reports and provides a form to create new ones. """ use ToweropsWeb, :live_view alias Towerops.Reports alias Towerops.Reports.Report alias Towerops.Workers.ReportWorker @impl true def mount(_params, _session, socket) do organization = socket.assigns.current_scope.organization reports = Reports.list_reports(organization.id) {:ok, socket |> assign(:page_title, "Reports") |> assign(:organization_id, organization.id) |> assign(:reports, reports) |> assign(:show_form, false) |> assign(:form, to_form(Report.changeset(%Report{}, %{})))} end @impl true def handle_event("show_form", _, socket) do {:noreply, assign(socket, show_form: true)} end @impl true def handle_event("hide_form", _, socket) do {:noreply, assign(socket, show_form: false)} end @impl true def handle_event("save", %{"report" => params}, socket) do recipients = (params["recipients_text"] || "") |> String.split([",", "\n", ";"]) |> Enum.map(&String.trim/1) |> Enum.reject(&(&1 == "")) attrs = params |> Map.put("recipients", recipients) |> Map.put("organization_id", socket.assigns.organization_id) |> Map.put("created_by_id", socket.assigns.current_scope.user.id) |> Map.put("schedule", %{"type" => params["schedule_type"] || "weekly"}) |> Map.put("scope", %{ "target" => params["scope_target"] || "all", "days" => String.to_integer(params["scope_days"] || "7") }) case Reports.create_report(attrs) do {:ok, _report} -> reports = Reports.list_reports(socket.assigns.organization_id) {:noreply, socket |> put_flash(:info, "Report created successfully") |> assign(:reports, reports) |> assign(:show_form, false) |> assign(:form, to_form(Report.changeset(%Report{}, %{})))} {:error, changeset} -> {:noreply, assign(socket, form: to_form(changeset))} end end @impl true def handle_event("toggle", %{"id" => id}, socket) do report = Reports.get_report!(id) case Reports.toggle_report(report) do {:ok, _} -> reports = Reports.list_reports(socket.assigns.organization_id) {:noreply, assign(socket, reports: reports)} {:error, _} -> {:noreply, put_flash(socket, :error, "Failed to toggle report")} end end @impl true def handle_event("delete", %{"id" => id}, socket) do report = Reports.get_report!(id) case Reports.delete_report(report) do {:ok, _} -> reports = Reports.list_reports(socket.assigns.organization_id) {:noreply, socket |> put_flash(:info, "Report deleted") |> assign(:reports, reports)} {:error, _} -> {:noreply, put_flash(socket, :error, "Failed to delete report")} end end @impl true def handle_event("run_now", %{"id" => id}, socket) do case %{"report_id" => id} |> ReportWorker.new() |> Oban.insert() do {:ok, _} -> {:noreply, put_flash(socket, :info, "Report queued for delivery")} {:error, _} -> {:noreply, put_flash(socket, :error, "Failed to queue report")} end end # -- Helpers -- @doc false def humanize_type("uptime_summary"), do: "Uptime Summary" def humanize_type("alert_history"), do: "Alert History" def humanize_type("capacity_trends"), do: "Capacity Trends" def humanize_type("rf_link_health"), do: "RF Link Health" def humanize_type(type), do: type @doc false def humanize_schedule(%{"type" => type}), do: String.capitalize(type) def humanize_schedule(_), do: "-" @doc false def format_recipients(recipients) when is_list(recipients), do: Enum.join(recipients, ", ") def format_recipients(_), do: "-" @doc false def status_badge("success"), do: {"Success", "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300"} def status_badge("failed"), do: {"Failed", "bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300"} def status_badge("partial_failure"), do: {"Partial", "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/50 dark:text-yellow-300"} def status_badge(_), do: {"Never Run", "bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300"} end