towerops/lib/towerops_web/live/org/settings_live.ex
Graham McIntire fc66109f19
Add default organization feature
Features:
- Users can set a default organization in org settings
- First organization created is automatically set as default
- Default org selector only shown if user has 2+ organizations
- Migration auto-sets first org as default for existing users

Database changes:
- Added is_default boolean field to organization_memberships
- Added partial index for efficient default org lookups
- Migration sets oldest org as default for each existing user

UI changes:
- Organization settings page shows default org toggle
- Green badge displayed when org is already default
- Button to set org as default if it's not current default

Backend:
- Organizations.set_default_organization/2 manages defaults
- Organizations.get_default_membership/1 retrieves default
- Auto-set first org as default in create_organization/3
2026-02-04 16:09:43 -06:00

104 lines
3.6 KiB
Elixir

defmodule ToweropsWeb.Org.SettingsLive do
@moduledoc false
use ToweropsWeb, :live_view
alias Towerops.Admin.AuditLogger
alias Towerops.Agents
alias Towerops.Organizations
@impl true
def mount(_params, _session, socket) do
organization = socket.assigns.current_scope.organization
user = socket.assigns.current_scope.user
available_agents = Agents.list_organization_agent_tokens(organization.id)
# Get assignment breakdown to show impact
assignment_breakdown = Agents.Stats.get_device_assignment_breakdown(organization.id)
# Get user's membership for this org to check if it's default
membership = Organizations.get_membership(organization.id, user.id)
# Count user's total organizations
user_orgs_count = user.id |> Organizations.list_user_organizations() |> length()
# Log organization data access
AuditLogger.log_org_data_accessed(nil, user.id, organization.id, "view_settings")
changeset = Organizations.change_organization(organization)
{:ok,
socket
|> assign(:organization, organization)
|> assign(:membership, membership)
|> assign(:user_orgs_count, user_orgs_count)
|> assign(:available_agents, available_agents)
|> assign(:assignment_breakdown, assignment_breakdown)
|> assign(:form, to_form(changeset))}
end
@impl true
def handle_event("validate", %{"organization" => org_params}, socket) do
changeset =
socket.assigns.organization
|> Organizations.change_organization(org_params)
|> Map.put(:action, :validate)
{:noreply, assign(socket, :form, to_form(changeset))}
end
@impl true
def handle_event("save", %{"organization" => org_params}, socket) do
case Organizations.update_organization(socket.assigns.organization, org_params) do
{:ok, organization} ->
changeset = Organizations.change_organization(organization)
{:noreply,
socket
|> put_flash(:info, "Settings saved successfully")
|> assign(:organization, organization)
|> assign(:form, to_form(changeset))}
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, :form, to_form(changeset))}
end
end
@impl true
def handle_event("apply_snmp_to_all", _params, socket) do
{count, _} = Organizations.apply_snmp_config_to_all_equipment(socket.assigns.organization.id)
{:noreply, put_flash(socket, :info, "Applied SNMP configuration to #{count} device records across all sites")}
end
@impl true
def handle_event("apply_agent_to_all", _params, socket) do
{count, _} = Organizations.apply_agent_to_all_equipment(socket.assigns.organization.id)
{:noreply, put_flash(socket, :info, "Applied default agent to #{count} device records across all sites")}
end
@impl true
def handle_event("toggle_default_org", _params, socket) do
user = socket.assigns.current_scope.user
organization = socket.assigns.organization
membership = socket.assigns.membership
if membership.is_default do
{:noreply, put_flash(socket, :error, "This is already your default organization")}
else
case Organizations.set_default_organization(user.id, organization.id) do
{:ok, _} ->
# Refresh membership to reflect change
updated_membership = Organizations.get_membership(organization.id, user.id)
{:noreply,
socket
|> put_flash(:info, "#{organization.name} is now your default organization")
|> assign(:membership, updated_membership)}
{:error, _} ->
{:noreply, put_flash(socket, :error, "Failed to set default organization")}
end
end
end
end