mirror of
https://github.com/plausible/analytics.git
synced 2024-09-11 18:07:33 +03:00
Add CRM QoL improvements around usage info and linking Paddle (#4229)
* Link subscription status to Paddle profile whenever available * Display usage info directly in user form in CRM * Handle pageview limit fallback gracefully when rendering subscription quota * Remove usage page link from users list view in CRM * Add tests for usage page * Apply `raw/1` only to the one (and only) element of the list
This commit is contained in:
parent
dd1d74ccb7
commit
aadf528459
@ -606,6 +606,7 @@ if config_env() in [:dev, :staging, :prod, :test] do
|
|||||||
ecto_repo: Plausible.Repo,
|
ecto_repo: Plausible.Repo,
|
||||||
router: PlausibleWeb.Router,
|
router: PlausibleWeb.Router,
|
||||||
admin_title: "Plausible Admin",
|
admin_title: "Plausible Admin",
|
||||||
|
extensions: [Plausible.CrmExtensions],
|
||||||
resources: [
|
resources: [
|
||||||
auth: [
|
auth: [
|
||||||
resources: [
|
resources: [
|
||||||
|
@ -36,7 +36,6 @@ defmodule Plausible.Auth.UserAdmin do
|
|||||||
trial_expiry_date: %{name: "Trial expiry", value: &format_date(&1.trial_expiry_date)},
|
trial_expiry_date: %{name: "Trial expiry", value: &format_date(&1.trial_expiry_date)},
|
||||||
subscription_plan: %{value: &subscription_plan/1},
|
subscription_plan: %{value: &subscription_plan/1},
|
||||||
subscription_status: %{value: &subscription_status/1},
|
subscription_status: %{value: &subscription_status/1},
|
||||||
usage: %{value: &usage_link/1},
|
|
||||||
grace_period: %{value: &grace_period_status/1},
|
grace_period: %{value: &grace_period_status/1},
|
||||||
accept_traffic_until: %{
|
accept_traffic_until: %{
|
||||||
name: "Accept traffic until",
|
name: "Accept traffic until",
|
||||||
@ -110,12 +109,7 @@ defmodule Plausible.Auth.UserAdmin do
|
|||||||
quota = PlausibleWeb.AuthView.subscription_quota(user.subscription)
|
quota = PlausibleWeb.AuthView.subscription_quota(user.subscription)
|
||||||
interval = PlausibleWeb.AuthView.subscription_interval(user.subscription)
|
interval = PlausibleWeb.AuthView.subscription_interval(user.subscription)
|
||||||
|
|
||||||
manage_url =
|
{:safe, ~s(<a href="#{manage_url(user.subscription)}">#{quota} \(#{interval}\)</a>)}
|
||||||
Plausible.Billing.PaddleApi.vendors_domain() <>
|
|
||||||
"/subscriptions/customers/manage/" <>
|
|
||||||
user.subscription.paddle_subscription_id
|
|
||||||
|
|
||||||
{:safe, ~s(<a href="#{manage_url}">#{quota} \(#{interval}\)</a>)}
|
|
||||||
else
|
else
|
||||||
"--"
|
"--"
|
||||||
end
|
end
|
||||||
@ -124,7 +118,13 @@ defmodule Plausible.Auth.UserAdmin do
|
|||||||
defp subscription_status(user) do
|
defp subscription_status(user) do
|
||||||
cond do
|
cond do
|
||||||
user.subscription ->
|
user.subscription ->
|
||||||
PlausibleWeb.AuthView.present_subscription_status(user.subscription.status)
|
status_str = PlausibleWeb.AuthView.present_subscription_status(user.subscription.status)
|
||||||
|
|
||||||
|
if user.subscription.paddle_subscription_id do
|
||||||
|
{:safe, ~s(<a href="#{manage_url(user.subscription)}">#{status_str}</a>)}
|
||||||
|
else
|
||||||
|
status_str
|
||||||
|
end
|
||||||
|
|
||||||
Plausible.Users.on_trial?(user) ->
|
Plausible.Users.on_trial?(user) ->
|
||||||
"On trial"
|
"On trial"
|
||||||
@ -134,13 +134,9 @@ defmodule Plausible.Auth.UserAdmin do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
on_ee do
|
defp manage_url(%{paddle_subscription_id: paddle_id} = _subscription) do
|
||||||
defp usage_link(user) do
|
Plausible.Billing.PaddleApi.vendors_domain() <>
|
||||||
path = PlausibleWeb.Router.Helpers.admin_path(PlausibleWeb.Endpoint, :usage, user.id)
|
"/subscriptions/customers/manage/" <> paddle_id
|
||||||
{:safe, ~s(<a href="#{path}">Usage</a>)}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
defp usage_link(_), do: nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp format_date(nil), do: "--"
|
defp format_date(nil), do: "--"
|
||||||
|
32
lib/plausible/crm_extensions.ex
Normal file
32
lib/plausible/crm_extensions.ex
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
defmodule Plausible.CrmExtensions do
|
||||||
|
@moduledoc """
|
||||||
|
Extensions for Kaffy CRM
|
||||||
|
"""
|
||||||
|
|
||||||
|
use Plausible
|
||||||
|
|
||||||
|
on_ee do
|
||||||
|
def javascripts(%{assigns: %{context: "auth", resource: "user", entry: %{} = user}}) do
|
||||||
|
[
|
||||||
|
Phoenix.HTML.raw("""
|
||||||
|
<script type="text/javascript">
|
||||||
|
(async () => {
|
||||||
|
const response = await fetch("/crm/auth/user/#{user.id}/usage?embed=true")
|
||||||
|
const usageHTML = await response.text()
|
||||||
|
const cardBody = document.querySelector(".card-body")
|
||||||
|
if (cardBody) {
|
||||||
|
const usageDOM = document.createElement("div")
|
||||||
|
usageDOM.innerHTML = usageHTML
|
||||||
|
cardBody.prepend(usageDOM)
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
</script>
|
||||||
|
""")
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def javascripts(_) do
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end
|
@ -17,39 +17,47 @@ defmodule PlausibleWeb.AdminController do
|
|||||||
team_members: Quota.Limits.team_member_limit(user)
|
team_members: Quota.Limits.team_member_limit(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
html_response = usage_and_limits_html(user, usage, limits)
|
html_response = usage_and_limits_html(user, usage, limits, params["embed"] == "true")
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_resp_content_type("text/html")
|
|> put_resp_content_type("text/html")
|
||||||
|> send_resp(200, html_response)
|
|> send_resp(200, html_response)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp usage_and_limits_html(user, usage, limits) do
|
defp usage_and_limits_html(user, usage, limits, embed?) do
|
||||||
"""
|
content = """
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Usage - user:#{user.id}</title>
|
|
||||||
<style>
|
|
||||||
ul, li {margin-top: 10px;}
|
|
||||||
body {padding-top: 10px;}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Sites: <b>#{usage.sites}</b> / #{limits.sites}</li>
|
<li>Sites: <b>#{usage.sites}</b> / #{limits.sites}</li>
|
||||||
<li>Team members: <b>#{usage.team_members}</b> / #{limits.team_members}</li>
|
<li>Team members: <b>#{usage.team_members}</b> / #{limits.team_members}</li>
|
||||||
<li>Features: #{features_usage(usage.features)}</li>
|
<li>Features: #{features_usage(usage.features)}</li>
|
||||||
<li>Monthly pageviews: #{monthly_pageviews_usage(usage.monthly_pageviews, limits.monthly_pageviews)}</li>
|
<li>Monthly pageviews: #{monthly_pageviews_usage(usage.monthly_pageviews, limits.monthly_pageviews)}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if embed? do
|
||||||
|
content
|
||||||
|
else
|
||||||
|
"""
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Usage - user:#{user.id}</title>
|
||||||
|
<style>
|
||||||
|
ul, li {margin-top: 10px;}
|
||||||
|
body {padding-top: 10px;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
#{content}
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp features_usage(features_module_list) do
|
defp features_usage(features_module_list) do
|
||||||
|
@ -9,14 +9,20 @@ defmodule PlausibleWeb.AuthView do
|
|||||||
def subscription_quota(nil, _options), do: "Free trial"
|
def subscription_quota(nil, _options), do: "Free trial"
|
||||||
|
|
||||||
def subscription_quota(subscription, options) do
|
def subscription_quota(subscription, options) do
|
||||||
subscription
|
pageview_limit = Plausible.Billing.Quota.Limits.monthly_pageview_limit(subscription)
|
||||||
|> Plausible.Billing.Quota.Limits.monthly_pageview_limit()
|
|
||||||
|> PlausibleWeb.StatsView.large_number_format()
|
quota =
|
||||||
|> then(fn quota ->
|
if pageview_limit == :unlimited do
|
||||||
if Keyword.get(options, :format) == :long,
|
"unlimited"
|
||||||
do: "#{quota} pageviews",
|
else
|
||||||
else: quota
|
PlausibleWeb.StatsView.large_number_format(pageview_limit)
|
||||||
end)
|
end
|
||||||
|
|
||||||
|
if Keyword.get(options, :format) == :long do
|
||||||
|
"#{quota} pageviews"
|
||||||
|
else
|
||||||
|
quota
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def subscription_interval(subscription) do
|
def subscription_interval(subscription) do
|
||||||
|
@ -35,7 +35,7 @@ defmodule PlausibleWeb.StatsView do
|
|||||||
"#{billions}B"
|
"#{billions}B"
|
||||||
end
|
end
|
||||||
|
|
||||||
true ->
|
is_integer(n) ->
|
||||||
Integer.to_string(n)
|
Integer.to_string(n)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -11,6 +11,20 @@ defmodule PlausibleWeb.AdminControllerTest do
|
|||||||
conn = get(conn, "/crm/auth/user/1/usage")
|
conn = get(conn, "/crm/auth/user/1/usage")
|
||||||
assert response(conn, 403) == "Not allowed"
|
assert response(conn, 403) == "Not allowed"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag :ee_only
|
||||||
|
test "returns usage data as a standalone page", %{conn: conn, user: user} do
|
||||||
|
patch_env(:super_admin_user_ids, [user.id])
|
||||||
|
conn = get(conn, "/crm/auth/user/#{user.id}/usage")
|
||||||
|
assert response(conn, 200) =~ "<html"
|
||||||
|
end
|
||||||
|
|
||||||
|
@tag :ee_only
|
||||||
|
test "returns usage data in embeddable form when requested", %{conn: conn, user: user} do
|
||||||
|
patch_env(:super_admin_user_ids, [user.id])
|
||||||
|
conn = get(conn, "/crm/auth/user/#{user.id}/usage?embed=true")
|
||||||
|
refute response(conn, 200) =~ "<html"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST /crm/sites/site/:site_id" do
|
describe "POST /crm/sites/site/:site_id" do
|
||||||
|
Loading…
Reference in New Issue
Block a user