diff --git a/lib/towerops/organizations.ex b/lib/towerops/organizations.ex index e663a6be..4c048d28 100644 --- a/lib/towerops/organizations.ex +++ b/lib/towerops/organizations.ex @@ -93,6 +93,13 @@ defmodule Towerops.Organizations do end defp do_create_organization(attrs, user_id) do + # Check if user has any existing organizations + has_existing_orgs = + Repo.exists?( + from m in Membership, + where: m.user_id == ^user_id + ) + multi = Ecto.Multi.new() |> Ecto.Multi.insert(:organization, Organization.changeset(%Organization{}, attrs)) @@ -100,27 +107,16 @@ defmodule Towerops.Organizations do Membership.changeset(%Membership{}, %{ organization_id: organization.id, user_id: user_id, - role: :owner + role: :owner, + # Set as default if this is the user's first organization + is_default: !has_existing_orgs }) end) - |> Ecto.Multi.run(:set_default, fn _repo, %{organization: organization} -> - # Set this as the user's default organization if they don't have one - user = Towerops.Accounts.get_user!(user_id) - - if user.default_organization_id == nil do - user - |> Ecto.Changeset.change(default_organization_id: organization.id) - |> Repo.update() - else - {:ok, user} - end - end) case Repo.transaction(multi) do {:ok, %{organization: organization}} -> {:ok, organization} {:error, :organization, changeset, _} -> {:error, changeset} {:error, :membership, changeset, _} -> {:error, changeset} - {:error, :set_default, changeset, _} -> {:error, changeset} end end @@ -273,6 +269,52 @@ defmodule Towerops.Organizations do Repo.delete(membership) end + @doc """ + Gets the user's default organization membership. + """ + def get_default_membership(user_id) do + Repo.one( + from m in Membership, + where: m.user_id == ^user_id and m.is_default == true, + preload: [:organization] + ) + end + + @doc """ + Sets an organization as the user's default. + Clears any existing default and sets the new one. + """ + def set_default_organization(user_id, organization_id) do + # Verify user has access to this organization + if user_has_access?(user_id, organization_id) do + multi = + Ecto.Multi.new() + |> Ecto.Multi.update_all(:clear_old_default, clear_default_query(user_id), set: [is_default: false]) + |> Ecto.Multi.update_all( + :set_new_default, + set_default_query(user_id, organization_id), + set: [is_default: true] + ) + + case Repo.transaction(multi) do + {:ok, _} -> {:ok, get_organization!(organization_id)} + {:error, _, changeset, _} -> {:error, changeset} + end + else + {:error, :not_found} + end + end + + defp clear_default_query(user_id) do + from m in Membership, + where: m.user_id == ^user_id and m.is_default == true + end + + defp set_default_query(user_id, organization_id) do + from m in Membership, + where: m.user_id == ^user_id and m.organization_id == ^organization_id + end + ## Invitations @doc """ diff --git a/lib/towerops/organizations/membership.ex b/lib/towerops/organizations/membership.ex index 56af12e2..a4eb42a0 100644 --- a/lib/towerops/organizations/membership.ex +++ b/lib/towerops/organizations/membership.ex @@ -16,6 +16,7 @@ defmodule Towerops.Organizations.Membership do @foreign_key_type :binary_id schema "organization_memberships" do field :role, Ecto.Enum, values: [:owner, :admin, :member, :viewer] + field :is_default, :boolean, default: false belongs_to :organization, Organization belongs_to :user, User @@ -26,6 +27,7 @@ defmodule Towerops.Organizations.Membership do @type t :: %__MODULE__{ id: Ecto.UUID.t(), role: :owner | :admin | :member | :viewer, + is_default: boolean(), organization_id: Ecto.UUID.t(), organization: NotLoaded.t() | Organization.t(), user_id: Ecto.UUID.t(), @@ -37,7 +39,7 @@ defmodule Towerops.Organizations.Membership do @doc false def changeset(membership, attrs) do membership - |> cast(attrs, [:role, :organization_id, :user_id]) + |> cast(attrs, [:role, :organization_id, :user_id, :is_default]) |> validate_required([:role, :organization_id, :user_id]) |> foreign_key_constraint(:organization_id) |> foreign_key_constraint(:user_id) diff --git a/lib/towerops_web/live/org/settings_live.ex b/lib/towerops_web/live/org/settings_live.ex index 67e04b44..6d848ce2 100644 --- a/lib/towerops_web/live/org/settings_live.ex +++ b/lib/towerops_web/live/org/settings_live.ex @@ -15,6 +15,12 @@ defmodule ToweropsWeb.Org.SettingsLive do # 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") @@ -23,6 +29,8 @@ defmodule ToweropsWeb.Org.SettingsLive do {: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))} @@ -68,4 +76,29 @@ defmodule ToweropsWeb.Org.SettingsLive do {: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 diff --git a/lib/towerops_web/live/org/settings_live.html.heex b/lib/towerops_web/live/org/settings_live.html.heex index 8e88dca6..a72f65f0 100644 --- a/lib/towerops_web/live/org/settings_live.html.heex +++ b/lib/towerops_web/live/org/settings_live.html.heex @@ -48,6 +48,47 @@ /> + + <%= if @user_orgs_count > 1 do %> + +
+ Set this organization as your default. When you log in, you'll be directed to your default organization. +
++ This is your default organization +
++ Click to make this your default organization +
+ <% end %> +