add default org
This commit is contained in:
parent
1ba55ce451
commit
949cfaf4e0
5 changed files with 104 additions and 5 deletions
|
|
@ -13,6 +13,7 @@ defmodule Towerops.Accounts.User do
|
||||||
|
|
||||||
alias Ecto.Association.NotLoaded
|
alias Ecto.Association.NotLoaded
|
||||||
alias Towerops.Organizations.Membership
|
alias Towerops.Organizations.Membership
|
||||||
|
alias Towerops.Organizations.Organization
|
||||||
|
|
||||||
@primary_key {:id, :binary_id, autogenerate: true}
|
@primary_key {:id, :binary_id, autogenerate: true}
|
||||||
@foreign_key_type :binary_id
|
@foreign_key_type :binary_id
|
||||||
|
|
@ -36,6 +37,7 @@ defmodule Towerops.Accounts.User do
|
||||||
|
|
||||||
has_many :memberships, Membership
|
has_many :memberships, Membership
|
||||||
has_many :organizations, through: [:memberships, :organization]
|
has_many :organizations, through: [:memberships, :organization]
|
||||||
|
belongs_to :default_organization, Organization
|
||||||
|
|
||||||
timestamps(type: :utc_datetime)
|
timestamps(type: :utc_datetime)
|
||||||
end
|
end
|
||||||
|
|
@ -55,7 +57,7 @@ defmodule Towerops.Accounts.User do
|
||||||
totp_verified_at: DateTime.t() | nil,
|
totp_verified_at: DateTime.t() | nil,
|
||||||
last_sudo_at: DateTime.t() | nil,
|
last_sudo_at: DateTime.t() | nil,
|
||||||
memberships: NotLoaded.t() | [Membership.t()],
|
memberships: NotLoaded.t() | [Membership.t()],
|
||||||
organizations: NotLoaded.t() | [Towerops.Organizations.Organization.t()],
|
organizations: NotLoaded.t() | [Organization.t()],
|
||||||
inserted_at: DateTime.t(),
|
inserted_at: DateTime.t(),
|
||||||
updated_at: DateTime.t()
|
updated_at: DateTime.t()
|
||||||
}
|
}
|
||||||
|
|
@ -107,10 +109,11 @@ defmodule Towerops.Accounts.User do
|
||||||
"""
|
"""
|
||||||
def profile_changeset(user, attrs) do
|
def profile_changeset(user, attrs) do
|
||||||
user
|
user
|
||||||
|> cast(attrs, [:first_name, :last_name, :timezone])
|
|> cast(attrs, [:first_name, :last_name, :timezone, :default_organization_id])
|
||||||
|> validate_length(:first_name, max: 100)
|
|> validate_length(:first_name, max: 100)
|
||||||
|> validate_length(:last_name, max: 100)
|
|> validate_length(:last_name, max: 100)
|
||||||
|> validate_length(:timezone, max: 100)
|
|> validate_length(:timezone, max: 100)
|
||||||
|
|> foreign_key_constraint(:default_organization_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
|
|
||||||
|
|
@ -287,8 +287,17 @@ defmodule ToweropsWeb.UserSettingsLive do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp assign_default_organization(socket) do
|
defp assign_default_organization(socket) do
|
||||||
|
user = socket.assigns.current_scope.user
|
||||||
organizations = socket.assigns.organizations
|
organizations = socket.assigns.organizations
|
||||||
default_org = List.first(organizations)
|
|
||||||
|
# Get user's preferred default org, or fall back to first org
|
||||||
|
default_org =
|
||||||
|
if user.default_organization_id do
|
||||||
|
Enum.find(organizations, &(&1.id == user.default_organization_id)) || List.first(organizations)
|
||||||
|
else
|
||||||
|
List.first(organizations)
|
||||||
|
end
|
||||||
|
|
||||||
assign(socket, :default_organization, default_org)
|
assign(socket, :default_organization, default_org)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -251,6 +251,48 @@
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div :if={@organizations != []} class="col-span-full">
|
||||||
|
<label
|
||||||
|
for="default_organization_id"
|
||||||
|
class="block text-sm/6 font-medium text-gray-900 dark:text-white"
|
||||||
|
>
|
||||||
|
Default Organization
|
||||||
|
</label>
|
||||||
|
<p class="mt-1 text-sm/6 text-gray-500 dark:text-gray-400">
|
||||||
|
Choose which organization you see by default when logging in or navigating to pages without an organization context.
|
||||||
|
</p>
|
||||||
|
<div class="mt-2 grid grid-cols-1">
|
||||||
|
<select
|
||||||
|
name={@profile_form[:default_organization_id].name}
|
||||||
|
id="default_organization_id"
|
||||||
|
class="col-start-1 row-start-1 w-full appearance-none rounded-md bg-white py-1.5 pr-8 pl-3 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6 dark:bg-white/5 dark:text-white dark:outline-white/10 dark:*:bg-gray-800 dark:focus:outline-indigo-500"
|
||||||
|
>
|
||||||
|
<option value="">None (use first organization)</option>
|
||||||
|
<%= for org <- @organizations do %>
|
||||||
|
<option
|
||||||
|
value={org.id}
|
||||||
|
selected={@profile_form[:default_organization_id].value == org.id}
|
||||||
|
>
|
||||||
|
{org.name}
|
||||||
|
</option>
|
||||||
|
<% end %>
|
||||||
|
</select>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="currentColor"
|
||||||
|
data-slot="icon"
|
||||||
|
aria-hidden="true"
|
||||||
|
class="pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-gray-400 sm:size-4"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-8 flex">
|
<div class="mt-8 flex">
|
||||||
|
|
|
||||||
|
|
@ -426,6 +426,7 @@ defmodule ToweropsWeb.UserAuth do
|
||||||
conn
|
conn
|
||||||
|> assign(:current_scope, Scope.put_organization(scope, organization))
|
|> assign(:current_scope, Scope.put_organization(scope, organization))
|
||||||
|> assign(:current_membership, membership)
|
|> assign(:current_membership, membership)
|
||||||
|
|> put_session(:current_organization_id, organization.id)
|
||||||
else
|
else
|
||||||
conn
|
conn
|
||||||
|> put_flash(:error, "You don't have access to this organization.")
|
|> put_flash(:error, "You don't have access to this organization.")
|
||||||
|
|
@ -673,13 +674,45 @@ defmodule ToweropsWeb.UserAuth do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp find_user_organization(org_id, _user_id) when is_binary(org_id) do
|
defp find_user_organization(org_id, user_id) when is_binary(org_id) do
|
||||||
|
# Try session org first
|
||||||
|
org =
|
||||||
|
try do
|
||||||
|
Towerops.Organizations.get_organization!(org_id)
|
||||||
|
rescue
|
||||||
|
Ecto.NoResultsError -> nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Verify user has access to this org
|
||||||
|
if org do
|
||||||
|
membership = Towerops.Organizations.get_membership(org.id, user_id)
|
||||||
|
if membership, do: org
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp find_user_organization(_org_id, user_id) do
|
||||||
|
user = Accounts.get_user(user_id)
|
||||||
|
default_org_id = user && user.default_organization_id
|
||||||
|
|
||||||
|
# Try user's default organization first
|
||||||
|
with true <- not is_nil(default_org_id),
|
||||||
|
default_org = try_get_organization(default_org_id),
|
||||||
|
true <- not is_nil(default_org),
|
||||||
|
membership = Towerops.Organizations.get_membership(default_org.id, user_id),
|
||||||
|
true <- not is_nil(membership) do
|
||||||
|
default_org
|
||||||
|
else
|
||||||
|
_ -> fallback_to_first_org(user_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp try_get_organization(org_id) do
|
||||||
Towerops.Organizations.get_organization!(org_id)
|
Towerops.Organizations.get_organization!(org_id)
|
||||||
rescue
|
rescue
|
||||||
Ecto.NoResultsError -> nil
|
Ecto.NoResultsError -> nil
|
||||||
end
|
end
|
||||||
|
|
||||||
defp find_user_organization(_org_id, user_id) do
|
defp fallback_to_first_org(user_id) do
|
||||||
case Towerops.Organizations.list_user_organizations(user_id) do
|
case Towerops.Organizations.list_user_organizations(user_id) do
|
||||||
[first_org | _] -> first_org
|
[first_org | _] -> first_org
|
||||||
[] -> nil
|
[] -> nil
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
defmodule Towerops.Repo.Migrations.AddDefaultOrganizationToUsers do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:users) do
|
||||||
|
add :default_organization_id,
|
||||||
|
references(:organizations, type: :binary_id, on_delete: :nilify_all)
|
||||||
|
end
|
||||||
|
|
||||||
|
create index(:users, [:default_organization_id])
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Add table
Reference in a new issue