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 Towerops.Organizations.Membership
|
||||
alias Towerops.Organizations.Organization
|
||||
|
||||
@primary_key {:id, :binary_id, autogenerate: true}
|
||||
@foreign_key_type :binary_id
|
||||
|
|
@ -36,6 +37,7 @@ defmodule Towerops.Accounts.User do
|
|||
|
||||
has_many :memberships, Membership
|
||||
has_many :organizations, through: [:memberships, :organization]
|
||||
belongs_to :default_organization, Organization
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
|
|
@ -55,7 +57,7 @@ defmodule Towerops.Accounts.User do
|
|||
totp_verified_at: DateTime.t() | nil,
|
||||
last_sudo_at: DateTime.t() | nil,
|
||||
memberships: NotLoaded.t() | [Membership.t()],
|
||||
organizations: NotLoaded.t() | [Towerops.Organizations.Organization.t()],
|
||||
organizations: NotLoaded.t() | [Organization.t()],
|
||||
inserted_at: DateTime.t(),
|
||||
updated_at: DateTime.t()
|
||||
}
|
||||
|
|
@ -107,10 +109,11 @@ defmodule Towerops.Accounts.User do
|
|||
"""
|
||||
def profile_changeset(user, attrs) do
|
||||
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(:last_name, max: 100)
|
||||
|> validate_length(:timezone, max: 100)
|
||||
|> foreign_key_constraint(:default_organization_id)
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
|
|
|||
|
|
@ -287,8 +287,17 @@ defmodule ToweropsWeb.UserSettingsLive do
|
|||
end
|
||||
|
||||
defp assign_default_organization(socket) do
|
||||
user = socket.assigns.current_scope.user
|
||||
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)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -251,6 +251,48 @@
|
|||
</svg>
|
||||
</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 class="mt-8 flex">
|
||||
|
|
|
|||
|
|
@ -426,6 +426,7 @@ defmodule ToweropsWeb.UserAuth do
|
|||
conn
|
||||
|> assign(:current_scope, Scope.put_organization(scope, organization))
|
||||
|> assign(:current_membership, membership)
|
||||
|> put_session(:current_organization_id, organization.id)
|
||||
else
|
||||
conn
|
||||
|> put_flash(:error, "You don't have access to this organization.")
|
||||
|
|
@ -673,13 +674,45 @@ defmodule ToweropsWeb.UserAuth do
|
|||
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)
|
||||
rescue
|
||||
Ecto.NoResultsError -> nil
|
||||
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
|
||||
[first_org | _] -> first_org
|
||||
[] -> 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