CRITICAL: - Membership: remove :role/:org_id/:user_id from mass-assignment cast; use explicit create_changeset/4 and role_update_changeset/2 - GraphQL member resolver: add authorize_invite/3 checking admin/owner role and role hierarchy - REST invitations controller: add auth check for invite creation HIGH: - ApiToken: remove :organization_id/:user_id from cast; use explicit create_changeset/4 MEDIUM: - Move 8 LiveView Ecto queries into context modules (Admin, Alerts, Coverages, OnCall, Snmp) - Replace Process.put/Process.get with socket assigns for unresolved_alert_count (user_auth + layouts + 50 templates) - Add batch get_utilization_for_interfaces/1 to eliminate N+1 capacity queries in device show - Replace Process.sleep with Process.monitor/assert_receive or Process.send_after in 5 test files LOW: - Add handle_params/3 to UserResetPasswordLive, UserRegistrationLive, StatusPageLive - Remove redundant Repo.preload calls; add preloads to list_site_devices/1 - Fix @impl annotations and credo nesting warnings
51 lines
1.9 KiB
Text
51 lines
1.9 KiB
Text
<Layouts.authenticated
|
|
flash={@flash}
|
|
current_scope={@current_scope}
|
|
unresolved_alert_count={assigns[:unresolved_alert_count] || 0}
|
|
>
|
|
<.header>
|
|
{@page_title}
|
|
<:actions>
|
|
<.button navigate={~p"/orgs/new"} variant="primary">
|
|
<.icon name="hero-plus" class="h-5 w-5" /> New Organization
|
|
</.button>
|
|
</:actions>
|
|
</.header>
|
|
|
|
<div :if={@organizations != []} class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
<form
|
|
:for={org <- @organizations}
|
|
method="post"
|
|
action={~p"/orgs/switch"}
|
|
>
|
|
<input type="hidden" name="_csrf_token" value={Phoenix.Controller.get_csrf_token()} />
|
|
<input type="hidden" name="org_id" value={org.id} />
|
|
<button
|
|
type="submit"
|
|
class="relative w-full overflow-hidden rounded-lg border border-gray-200 bg-white p-6 shadow-sm hover:shadow-md transition-shadow dark:border-white/10 dark:bg-gray-800/50 cursor-pointer text-left"
|
|
>
|
|
<h2 class="text-xl font-semibold text-gray-900 dark:text-white">{org.name}</h2>
|
|
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
|
|
{Enum.find(org.memberships, &(&1.user_id == @current_scope.user.id)).role
|
|
|> to_string()
|
|
|> String.capitalize()}
|
|
</p>
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div :if={@organizations == []} class="text-center py-16">
|
|
<.icon name="hero-building-office" class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-600" />
|
|
<h3 class="mt-4 text-lg font-semibold text-gray-900 dark:text-white">
|
|
{t("No organizations")}
|
|
</h3>
|
|
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
|
|
{t("Get started by creating a new organization.")}
|
|
</p>
|
|
<div class="mt-6">
|
|
<.button navigate={~p"/orgs/new"} variant="primary">
|
|
<.icon name="hero-plus" class="h-5 w-5" /> New Organization
|
|
</.button>
|
|
</div>
|
|
</div>
|
|
</Layouts.authenticated>
|