feat: collapse and dim inactive billing integrations
When a billing provider is active (enabled), the other billing providers are dimmed and collapsed to a single row showing a lock icon and which provider is active. This reinforces the mutually exclusive constraint. Also fixes status emoji accumulating in page title (JS regex needed unicode flag) and adds missing page_title to 8 LiveViews.
This commit is contained in:
parent
f2608e8f16
commit
031427a247
2 changed files with 232 additions and 196 deletions
|
|
@ -176,6 +176,7 @@ defmodule ToweropsWeb.Org.SettingsLive do
|
|||
|> assign(:providers, @providers)
|
||||
|> assign(:billing_provider_ids, @billing_provider_ids)
|
||||
|> assign(:integrations, %{})
|
||||
|> assign(:active_billing_provider, nil)
|
||||
|> assign(:configuring, nil)
|
||||
|> assign(:integration_form, nil)
|
||||
|> assign(:test_result, nil)
|
||||
|
|
@ -205,7 +206,10 @@ defmodule ToweropsWeb.Org.SettingsLive do
|
|||
|
||||
defp maybe_load_integrations(socket, "integrations") do
|
||||
integrations = load_integrations(socket.assigns.organization.id)
|
||||
assign(socket, :integrations, integrations)
|
||||
|
||||
socket
|
||||
|> assign(:integrations, integrations)
|
||||
|> assign(:active_billing_provider, active_billing_provider(integrations))
|
||||
end
|
||||
|
||||
defp maybe_load_integrations(socket, _tab), do: socket
|
||||
|
|
@ -480,6 +484,7 @@ defmodule ToweropsWeb.Org.SettingsLive do
|
|||
{:noreply,
|
||||
socket
|
||||
|> assign(:integrations, integrations)
|
||||
|> assign(:active_billing_provider, active_billing_provider(integrations))
|
||||
|> assign(:configuring, nil)
|
||||
|> assign(:integration_form, nil)
|
||||
|> assign(:test_result, nil)
|
||||
|
|
@ -513,6 +518,7 @@ defmodule ToweropsWeb.Org.SettingsLive do
|
|||
{:noreply,
|
||||
socket
|
||||
|> assign(:integrations, integrations)
|
||||
|> assign(:active_billing_provider, active_billing_provider(integrations))
|
||||
|> put_flash(:info, t("Webhook secret saved"))}
|
||||
|
||||
{:error, _changeset} ->
|
||||
|
|
@ -535,6 +541,7 @@ defmodule ToweropsWeb.Org.SettingsLive do
|
|||
{:noreply,
|
||||
socket
|
||||
|> assign(:integrations, integrations)
|
||||
|> assign(:active_billing_provider, active_billing_provider(integrations))
|
||||
|> put_flash(:info, t("Integration updated"))}
|
||||
|
||||
{:error, _changeset} ->
|
||||
|
|
@ -649,6 +656,15 @@ defmodule ToweropsWeb.Org.SettingsLive do
|
|||
|> Map.new(fn integration -> {integration.provider, integration} end)
|
||||
end
|
||||
|
||||
defp active_billing_provider(integrations) do
|
||||
Enum.find_value(@billing_provider_ids, fn id ->
|
||||
case Map.get(integrations, id) do
|
||||
%{enabled: true} -> id
|
||||
_ -> nil
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp get_current_integration(socket) do
|
||||
provider = socket.assigns.configuring
|
||||
|
||||
|
|
|
|||
|
|
@ -865,6 +865,9 @@
|
|||
<%= if @active_tab == "integrations" do %>
|
||||
<div class="mt-8 space-y-5">
|
||||
<%= for {provider, idx} <- Enum.with_index(@providers) do %>
|
||||
<% billing_locked? =
|
||||
@active_billing_provider != nil and provider.id in @billing_provider_ids and
|
||||
provider.id != @active_billing_provider %>
|
||||
<%!-- Section headers --%>
|
||||
<%= if idx == 0 do %>
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
|
|
@ -888,7 +891,11 @@
|
|||
<% end %>
|
||||
<div class={[
|
||||
"relative overflow-hidden rounded-xl border bg-white shadow-sm transition-all dark:bg-white/[0.03]",
|
||||
if(billing_locked?, do: "opacity-50"),
|
||||
cond do
|
||||
billing_locked? ->
|
||||
"border-gray-200 dark:border-white/10"
|
||||
|
||||
@integrations[provider.id] && @integrations[provider.id].last_sync_status == "failed" ->
|
||||
"border-red-200 dark:border-red-500/30"
|
||||
|
||||
|
|
@ -963,215 +970,228 @@
|
|||
</div>
|
||||
|
||||
<%!-- === Actions (top right) === --%>
|
||||
<div class="flex items-center gap-4 shrink-0 ml-4">
|
||||
<%= if integration = @integrations[provider.id] do %>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class={[
|
||||
"text-xs font-medium",
|
||||
if(integration.enabled,
|
||||
do: "text-green-700 dark:text-green-400",
|
||||
else: "text-gray-500 dark:text-gray-400"
|
||||
)
|
||||
]}>
|
||||
{if integration.enabled, do: "Enabled", else: "Disabled"}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
phx-click="toggle_enabled"
|
||||
phx-value-provider={provider.id}
|
||||
class={[
|
||||
"relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2 dark:focus:ring-offset-gray-900",
|
||||
if(integration.enabled,
|
||||
do: "bg-indigo-600",
|
||||
else: "bg-gray-200 dark:bg-gray-700"
|
||||
)
|
||||
]}
|
||||
>
|
||||
<%= if billing_locked? do %>
|
||||
<div class="shrink-0 ml-4">
|
||||
<span class="inline-flex items-center gap-1.5 rounded-full bg-gray-100 px-3 py-1 text-xs font-medium text-gray-500 dark:bg-white/5 dark:text-gray-400">
|
||||
<.icon name="hero-lock-closed-mini" class="h-3.5 w-3.5" />
|
||||
{Enum.find_value(@providers, fn p ->
|
||||
if p.id == @active_billing_provider, do: p.name
|
||||
end)} is active
|
||||
</span>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="flex items-center gap-4 shrink-0 ml-4">
|
||||
<%= if integration = @integrations[provider.id] do %>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class={[
|
||||
"pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out",
|
||||
if(integration.enabled, do: "translate-x-5", else: "translate-x-0")
|
||||
"text-xs font-medium",
|
||||
if(integration.enabled,
|
||||
do: "text-green-700 dark:text-green-400",
|
||||
else: "text-gray-500 dark:text-gray-400"
|
||||
)
|
||||
]}>
|
||||
{if integration.enabled, do: "Enabled", else: "Disabled"}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
phx-click="toggle_enabled"
|
||||
phx-value-provider={provider.id}
|
||||
class={[
|
||||
"relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2 dark:focus:ring-offset-gray-900",
|
||||
if(integration.enabled,
|
||||
do: "bg-indigo-600",
|
||||
else: "bg-gray-200 dark:bg-gray-700"
|
||||
)
|
||||
]}
|
||||
>
|
||||
<span class={[
|
||||
"pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out",
|
||||
if(integration.enabled, do: "translate-x-5", else: "translate-x-0")
|
||||
]}>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
id={"configure-#{provider.id}"}
|
||||
phx-click="configure"
|
||||
phx-value-provider={provider.id}
|
||||
class={[
|
||||
"rounded-lg px-4 py-2 text-sm font-semibold shadow-xs transition-colors",
|
||||
if(@configuring == provider.id,
|
||||
do:
|
||||
"bg-gray-100 text-gray-700 ring-1 ring-inset ring-gray-300 dark:bg-white/10 dark:text-gray-300 dark:ring-white/10",
|
||||
else:
|
||||
"bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:bg-white/10 dark:text-white dark:ring-white/10 dark:hover:bg-white/20"
|
||||
)
|
||||
]}
|
||||
>
|
||||
{if @configuring == provider.id, do: "Close", else: "Configure"}
|
||||
</button>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%!-- === Status Row (when configured, hidden for locked billing providers) === --%>
|
||||
<%= if !billing_locked? do %>
|
||||
<%= if integration = @integrations[provider.id] do %>
|
||||
<%!-- Failed sync banner --%>
|
||||
<%= if integration.last_sync_status == "failed" do %>
|
||||
<div class="mt-4 flex items-start gap-3 rounded-lg bg-red-50 px-4 py-3 dark:bg-red-900/20">
|
||||
<.icon
|
||||
name="hero-exclamation-triangle"
|
||||
class="h-5 w-5 shrink-0 text-red-500 dark:text-red-400 mt-0.5"
|
||||
/>
|
||||
<div>
|
||||
<p class="text-sm font-medium text-red-800 dark:text-red-300">
|
||||
{t("Last sync failed")}
|
||||
</p>
|
||||
<%= if integration.last_sync_message && integration.last_sync_message != "" do %>
|
||||
<p class="mt-0.5 text-sm text-red-700 dark:text-red-400">
|
||||
{integration.last_sync_message}
|
||||
</p>
|
||||
<% end %>
|
||||
<%= if integration.last_synced_at do %>
|
||||
<p class="mt-1 text-xs text-red-600/70 dark:text-red-400/60">
|
||||
<.timestamp
|
||||
datetime={integration.last_synced_at}
|
||||
timezone={@timezone}
|
||||
format="absolute"
|
||||
/>
|
||||
</p>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="mt-4 flex flex-wrap items-center gap-x-5 gap-y-2">
|
||||
<%!-- Status dot + label --%>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class={[
|
||||
"inline-block h-2 w-2 rounded-full",
|
||||
if(integration.enabled,
|
||||
do: "bg-green-500",
|
||||
else: "bg-gray-400 dark:bg-gray-500"
|
||||
)
|
||||
]} />
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400">
|
||||
{if integration.enabled, do: "Connected", else: "Paused"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<%!-- Last synced --%>
|
||||
<%= if integration.last_synced_at do %>
|
||||
<div class="flex items-center gap-1.5 text-sm text-gray-500 dark:text-gray-400">
|
||||
<.icon name="hero-clock" class="h-3.5 w-3.5" />
|
||||
<span>
|
||||
{t("Synced")}
|
||||
<.timestamp
|
||||
datetime={integration.last_synced_at}
|
||||
timezone={@timezone}
|
||||
format="absolute"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<% else %>
|
||||
<span class="text-sm italic text-gray-400 dark:text-gray-500">
|
||||
{t("Never synced")}
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<%!-- Sync status badge (success/partial) --%>
|
||||
<%= if integration.last_sync_status && integration.last_sync_status not in ["never", "failed"] do %>
|
||||
<span class={[
|
||||
"inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-xs font-medium",
|
||||
case integration.last_sync_status do
|
||||
"success" ->
|
||||
"bg-green-50 text-green-700 ring-1 ring-inset ring-green-600/20 dark:bg-green-500/10 dark:text-green-400 dark:ring-green-500/20"
|
||||
|
||||
"partial" ->
|
||||
"bg-yellow-50 text-yellow-700 ring-1 ring-inset ring-yellow-600/20 dark:bg-yellow-500/10 dark:text-yellow-400 dark:ring-yellow-500/20"
|
||||
|
||||
_ ->
|
||||
"bg-gray-50 text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-gray-500/10 dark:text-gray-400 dark:ring-gray-500/20"
|
||||
end
|
||||
]}>
|
||||
<%= if integration.last_sync_status == "success" do %>
|
||||
<.icon name="hero-check-circle-mini" class="h-3.5 w-3.5" />
|
||||
<% end %>
|
||||
{String.capitalize(integration.last_sync_status)}
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<%!-- Sync message (success/partial) --%>
|
||||
<%= if integration.last_sync_message && integration.last_sync_message != "" && integration.last_sync_status != "failed" do %>
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400">
|
||||
{integration.last_sync_message}
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<%!-- Next sync --%>
|
||||
<%= if integration.last_synced_at && integration.enabled do %>
|
||||
<span class="text-xs text-gray-400 dark:text-gray-500">
|
||||
Next in ~{next_sync_minutes(
|
||||
integration.last_synced_at,
|
||||
integration.sync_interval_minutes
|
||||
)}m
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<%!-- Sync schedule note --%>
|
||||
<%= if integration.enabled && provider.id == "pagerduty" do %>
|
||||
<span class="text-xs text-gray-400 dark:text-gray-500">
|
||||
· Event-driven (real-time)
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
id={"configure-#{provider.id}"}
|
||||
phx-click="configure"
|
||||
phx-value-provider={provider.id}
|
||||
class={[
|
||||
"rounded-lg px-4 py-2 text-sm font-semibold shadow-xs transition-colors",
|
||||
if(@configuring == provider.id,
|
||||
do:
|
||||
"bg-gray-100 text-gray-700 ring-1 ring-inset ring-gray-300 dark:bg-white/10 dark:text-gray-300 dark:ring-white/10",
|
||||
else:
|
||||
"bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:bg-white/10 dark:text-white dark:ring-white/10 dark:hover:bg-white/20"
|
||||
)
|
||||
]}
|
||||
>
|
||||
{if @configuring == provider.id, do: "Close", else: "Configure"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%!-- === Status Row (when configured) === --%>
|
||||
<%= if integration = @integrations[provider.id] do %>
|
||||
<%!-- Failed sync banner --%>
|
||||
<%= if integration.last_sync_status == "failed" do %>
|
||||
<div class="mt-4 flex items-start gap-3 rounded-lg bg-red-50 px-4 py-3 dark:bg-red-900/20">
|
||||
<.icon
|
||||
name="hero-exclamation-triangle"
|
||||
class="h-5 w-5 shrink-0 text-red-500 dark:text-red-400 mt-0.5"
|
||||
/>
|
||||
<div>
|
||||
<p class="text-sm font-medium text-red-800 dark:text-red-300">
|
||||
{t("Last sync failed")}
|
||||
</p>
|
||||
<%= if integration.last_sync_message && integration.last_sync_message != "" do %>
|
||||
<p class="mt-0.5 text-sm text-red-700 dark:text-red-400">
|
||||
{integration.last_sync_message}
|
||||
</p>
|
||||
<%!-- === Provider Links Row === --%>
|
||||
<%= if provider.id == "preseem" || provider.id == "gaiia" do %>
|
||||
<div class="mt-3 flex flex-wrap gap-3">
|
||||
<%= if provider.id == "preseem" do %>
|
||||
<.link
|
||||
navigate={
|
||||
~p"/orgs/#{@organization.slug}/settings/integrations/preseem/devices"
|
||||
}
|
||||
class="inline-flex items-center gap-1.5 rounded-md bg-gray-50 px-3 py-1.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-200 transition-colors hover:bg-gray-100 dark:bg-white/5 dark:text-gray-300 dark:ring-white/10 dark:hover:bg-white/10"
|
||||
>
|
||||
<.icon name="hero-cpu-chip" class="h-3.5 w-3.5" /> Manage Devices
|
||||
</.link>
|
||||
<.link
|
||||
navigate={~p"/insights?source=preseem"}
|
||||
class="inline-flex items-center gap-1.5 rounded-md bg-gray-50 px-3 py-1.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-200 transition-colors hover:bg-gray-100 dark:bg-white/5 dark:text-gray-300 dark:ring-white/10 dark:hover:bg-white/10"
|
||||
>
|
||||
<.icon name="hero-chart-bar" class="h-3.5 w-3.5" /> Network Insights
|
||||
</.link>
|
||||
<% end %>
|
||||
<%= if integration.last_synced_at do %>
|
||||
<p class="mt-1 text-xs text-red-600/70 dark:text-red-400/60">
|
||||
<.timestamp
|
||||
datetime={integration.last_synced_at}
|
||||
timezone={@timezone}
|
||||
format="absolute"
|
||||
/>
|
||||
</p>
|
||||
<%= if provider.id == "gaiia" do %>
|
||||
<.link
|
||||
navigate={
|
||||
~p"/orgs/#{@organization.slug}/settings/integrations/gaiia/mapping"
|
||||
}
|
||||
class="inline-flex items-center gap-1.5 rounded-md bg-gray-50 px-3 py-1.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-200 transition-colors hover:bg-gray-100 dark:bg-white/5 dark:text-gray-300 dark:ring-white/10 dark:hover:bg-white/10"
|
||||
>
|
||||
<.icon name="hero-arrows-right-left" class="h-3.5 w-3.5" /> Entity Mapping
|
||||
</.link>
|
||||
<.link
|
||||
navigate={
|
||||
~p"/orgs/#{@organization.slug}/settings/integrations/gaiia/reconciliation"
|
||||
}
|
||||
class="inline-flex items-center gap-1.5 rounded-md bg-gray-50 px-3 py-1.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-200 transition-colors hover:bg-gray-100 dark:bg-white/5 dark:text-gray-300 dark:ring-white/10 dark:hover:bg-white/10"
|
||||
>
|
||||
<.icon name="hero-scale" class="h-3.5 w-3.5" /> Reconciliation
|
||||
</.link>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="mt-4 flex flex-wrap items-center gap-x-5 gap-y-2">
|
||||
<%!-- Status dot + label --%>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class={[
|
||||
"inline-block h-2 w-2 rounded-full",
|
||||
if(integration.enabled,
|
||||
do: "bg-green-500",
|
||||
else: "bg-gray-400 dark:bg-gray-500"
|
||||
)
|
||||
]} />
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400">
|
||||
{if integration.enabled, do: "Connected", else: "Paused"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<%!-- Last synced --%>
|
||||
<%= if integration.last_synced_at do %>
|
||||
<div class="flex items-center gap-1.5 text-sm text-gray-500 dark:text-gray-400">
|
||||
<.icon name="hero-clock" class="h-3.5 w-3.5" />
|
||||
<span>
|
||||
{t("Synced")}
|
||||
<.timestamp
|
||||
datetime={integration.last_synced_at}
|
||||
timezone={@timezone}
|
||||
format="absolute"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<% else %>
|
||||
<span class="text-sm italic text-gray-400 dark:text-gray-500">
|
||||
{t("Never synced")}
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<%!-- Sync status badge (success/partial) --%>
|
||||
<%= if integration.last_sync_status && integration.last_sync_status not in ["never", "failed"] do %>
|
||||
<span class={[
|
||||
"inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-xs font-medium",
|
||||
case integration.last_sync_status do
|
||||
"success" ->
|
||||
"bg-green-50 text-green-700 ring-1 ring-inset ring-green-600/20 dark:bg-green-500/10 dark:text-green-400 dark:ring-green-500/20"
|
||||
|
||||
"partial" ->
|
||||
"bg-yellow-50 text-yellow-700 ring-1 ring-inset ring-yellow-600/20 dark:bg-yellow-500/10 dark:text-yellow-400 dark:ring-yellow-500/20"
|
||||
|
||||
_ ->
|
||||
"bg-gray-50 text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-gray-500/10 dark:text-gray-400 dark:ring-gray-500/20"
|
||||
end
|
||||
]}>
|
||||
<%= if integration.last_sync_status == "success" do %>
|
||||
<.icon name="hero-check-circle-mini" class="h-3.5 w-3.5" />
|
||||
<% end %>
|
||||
{String.capitalize(integration.last_sync_status)}
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<%!-- Sync message (success/partial) --%>
|
||||
<%= if integration.last_sync_message && integration.last_sync_message != "" && integration.last_sync_status != "failed" do %>
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400">
|
||||
{integration.last_sync_message}
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<%!-- Next sync --%>
|
||||
<%= if integration.last_synced_at && integration.enabled do %>
|
||||
<span class="text-xs text-gray-400 dark:text-gray-500">
|
||||
Next in ~{next_sync_minutes(
|
||||
integration.last_synced_at,
|
||||
integration.sync_interval_minutes
|
||||
)}m
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<%!-- Sync schedule note --%>
|
||||
<%= if integration.enabled && provider.id == "pagerduty" do %>
|
||||
<span class="text-xs text-gray-400 dark:text-gray-500">
|
||||
· Event-driven (real-time)
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%!-- === Provider Links Row === --%>
|
||||
<%= if provider.id == "preseem" || provider.id == "gaiia" do %>
|
||||
<div class="mt-3 flex flex-wrap gap-3">
|
||||
<%= if provider.id == "preseem" do %>
|
||||
<.link
|
||||
navigate={
|
||||
~p"/orgs/#{@organization.slug}/settings/integrations/preseem/devices"
|
||||
}
|
||||
class="inline-flex items-center gap-1.5 rounded-md bg-gray-50 px-3 py-1.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-200 transition-colors hover:bg-gray-100 dark:bg-white/5 dark:text-gray-300 dark:ring-white/10 dark:hover:bg-white/10"
|
||||
>
|
||||
<.icon name="hero-cpu-chip" class="h-3.5 w-3.5" /> Manage Devices
|
||||
</.link>
|
||||
<.link
|
||||
navigate={~p"/insights?source=preseem"}
|
||||
class="inline-flex items-center gap-1.5 rounded-md bg-gray-50 px-3 py-1.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-200 transition-colors hover:bg-gray-100 dark:bg-white/5 dark:text-gray-300 dark:ring-white/10 dark:hover:bg-white/10"
|
||||
>
|
||||
<.icon name="hero-chart-bar" class="h-3.5 w-3.5" /> Network Insights
|
||||
</.link>
|
||||
<% end %>
|
||||
<%= if provider.id == "gaiia" do %>
|
||||
<.link
|
||||
navigate={
|
||||
~p"/orgs/#{@organization.slug}/settings/integrations/gaiia/mapping"
|
||||
}
|
||||
class="inline-flex items-center gap-1.5 rounded-md bg-gray-50 px-3 py-1.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-200 transition-colors hover:bg-gray-100 dark:bg-white/5 dark:text-gray-300 dark:ring-white/10 dark:hover:bg-white/10"
|
||||
>
|
||||
<.icon name="hero-arrows-right-left" class="h-3.5 w-3.5" /> Entity Mapping
|
||||
</.link>
|
||||
<.link
|
||||
navigate={
|
||||
~p"/orgs/#{@organization.slug}/settings/integrations/gaiia/reconciliation"
|
||||
}
|
||||
class="inline-flex items-center gap-1.5 rounded-md bg-gray-50 px-3 py-1.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-200 transition-colors hover:bg-gray-100 dark:bg-white/5 dark:text-gray-300 dark:ring-white/10 dark:hover:bg-white/10"
|
||||
>
|
||||
<.icon name="hero-scale" class="h-3.5 w-3.5" /> Reconciliation
|
||||
</.link>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%!-- === Configure Panel === --%>
|
||||
<%= if @configuring == provider.id do %>
|
||||
<%= if !billing_locked? && @configuring == provider.id do %>
|
||||
<div class="border-t border-gray-200 bg-gray-50/50 px-6 py-6 dark:border-white/10 dark:bg-white/[0.02]">
|
||||
<%= if provider.id == "netbox" do %>
|
||||
<.form
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue