diff --git a/CHANGELOG.md b/CHANGELOG.md
index fbb60c66b6..3c23f77f50 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
- Improve password validation in registration and password reset forms
- Adds Gravatar profile image to navbar
- Enforce email reverification on update
+- Add Plugins API Tokens provisioning UI
### Removed
- Removed the nested custom event property breakdown UI when filtering by a goal in Goal Conversions
@@ -20,6 +21,7 @@ All notable changes to this project will be documented in this file.
- Limit the number of Goal Conversions shown on the dashboard and render a "Details" link when there are more entries to show
- Show Outbound Links / File Downloads / 404 Pages / Cloaked Links instead of Goal Conversions when filtering by the corresponding goal
- Require custom properties to be explicitly added from Site Settings > Custom Properties in order for them to show up on the dashboard
+- GA/SC sections moved to new settings: Integrations
### Fixed
- Only return `(none)` values in custom property breakdown for the first page (pagination) of results
diff --git a/lib/plausible/plugins/api/tokens.ex b/lib/plausible/plugins/api/tokens.ex
index ceb9b7a871..42f3e0bb49 100644
--- a/lib/plausible/plugins/api/tokens.ex
+++ b/lib/plausible/plugins/api/tokens.ex
@@ -36,4 +36,20 @@ defmodule Plausible.Plugins.API.Tokens do
{:error, :not_found}
end
end
+
+ @spec delete(Site.t(), String.t()) :: :ok
+ def delete(site, token_id) do
+ Repo.delete_all(from t in Token, where: t.site_id == ^site.id and t.id == ^token_id)
+ :ok
+ end
+
+ @spec list(Site.t()) :: {:ok, [Token.t()]}
+ def list(site) do
+ Repo.all(from t in Token, where: t.site_id == ^site.id, order_by: [desc: t.id])
+ end
+
+ @spec any?(Site.t()) :: boolean()
+ def any?(site) do
+ Repo.exists?(from(t in Token, where: t.site_id == ^site.id))
+ end
end
diff --git a/lib/plausible_web/components/generic.ex b/lib/plausible_web/components/generic.ex
index f00c227bd2..cf2036823b 100644
--- a/lib/plausible_web/components/generic.ex
+++ b/lib/plausible_web/components/generic.ex
@@ -4,10 +4,20 @@ defmodule PlausibleWeb.Components.Generic do
"""
use Phoenix.Component
- attr :title, :string, default: "Notice"
- attr :size, :atom, default: :sm
- attr :rest, :global
- slot :inner_block
+ attr(:slug, :string, required: true)
+
+ def docs_info(assigns) do
+ ~H"""
+
+
+
+ """
+ end
+
+ attr(:title, :string, default: "Notice")
+ attr(:size, :atom, default: :sm)
+ attr(:rest, :global)
+ slot(:inner_block)
def notice(assigns) do
~H"""
diff --git a/lib/plausible_web/components/google.ex b/lib/plausible_web/components/google.ex
new file mode 100644
index 0000000000..c8067e00f6
--- /dev/null
+++ b/lib/plausible_web/components/google.ex
@@ -0,0 +1,65 @@
+defmodule PlausibleWeb.Components.Google do
+ @moduledoc """
+ Google-related components
+ """
+ use Phoenix.Component
+ use Phoenix.HTML
+
+ attr(:to, :string, required: true)
+ attr(:id, :string, required: true)
+
+ def button(assigns) do
+ ~H"""
+ <%= button(id: @id, to: @to, class: "inline-flex pr-4 items-center border border-gray-100 shadow rounded-md focus:outline-none focus:ring-1 focus:ring-offset-1 focus:ring-gray-200 mt-8 hover:bg-gray-50 dark:hover:bg-gray-700") do %>
+ <.logo />
+
+ Continue with Google
+
+ <% end %>
+ """
+ end
+
+ def logo(assigns \\ %{}) do
+ ~H"""
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ """
+ end
+end
diff --git a/lib/plausible_web/components/settings.ex b/lib/plausible_web/components/settings.ex
new file mode 100644
index 0000000000..34f457479c
--- /dev/null
+++ b/lib/plausible_web/components/settings.ex
@@ -0,0 +1,12 @@
+defmodule PlausibleWeb.Components.Settings do
+ @moduledoc """
+ An umbrella module for the Integrations settings section
+ """
+ use Phoenix.Component
+ use Phoenix.HTML
+
+ import PlausibleWeb.Components.Generic
+
+ embed_templates("../templates/site/settings_search_console.html")
+ embed_templates("../templates/site/settings_google_import.html")
+end
diff --git a/lib/plausible_web/controllers/auth_controller.ex b/lib/plausible_web/controllers/auth_controller.ex
index b33303b824..a1f773503b 100644
--- a/lib/plausible_web/controllers/auth_controller.ex
+++ b/lib/plausible_web/controllers/auth_controller.ex
@@ -516,7 +516,7 @@ defmodule PlausibleWeb.AuthController do
site = Repo.get(Plausible.Site, site_id)
- redirect(conn, to: "/#{URI.encode_www_form(site.domain)}/settings/#{redirect_to}")
+ redirect(conn, to: "/#{URI.encode_www_form(site.domain)}/settings/integrations")
end
end
end
diff --git a/lib/plausible_web/controllers/site_controller.ex b/lib/plausible_web/controllers/site_controller.ex
index e69a596cde..88fba62ea8 100644
--- a/lib/plausible_web/controllers/site_controller.ex
+++ b/lib/plausible_web/controllers/site_controller.ex
@@ -166,17 +166,9 @@ defmodule PlausibleWeb.SiteController do
conn.assigns[:site]
|> Repo.preload([:custom_domain])
- imported_pageviews =
- if site.imported_data do
- Plausible.Stats.Clickhouse.imported_pageview_count(site)
- else
- 0
- end
-
conn
|> render("settings_general.html",
site: site,
- imported_pageviews: imported_pageviews,
changeset: Plausible.Site.changeset(site, %{}),
dogfood_page_path: "/:dashboard/settings/general",
layout: {PlausibleWeb.LayoutView, "site_settings.html"}
@@ -251,25 +243,6 @@ defmodule PlausibleWeb.SiteController do
)
end
- def settings_search_console(conn, _params) do
- site =
- conn.assigns[:site]
- |> Repo.preload([:google_auth, :custom_domain])
-
- search_console_domains =
- if site.google_auth do
- Plausible.Google.Api.fetch_verified_properties(site.google_auth)
- end
-
- conn
- |> render("settings_search_console.html",
- site: site,
- search_console_domains: search_console_domains,
- dogfood_page_path: "/:dashboard/settings/search-console",
- layout: {PlausibleWeb.LayoutView, "site_settings.html"}
- )
- end
-
def settings_email_reports(conn, _params) do
site = conn.assigns[:site] |> Repo.preload(:custom_domain)
@@ -308,6 +281,37 @@ defmodule PlausibleWeb.SiteController do
)
end
+ def settings_integrations(conn, _params) do
+ site =
+ conn.assigns.site
+ |> Repo.preload([:google_auth, :custom_domain])
+
+ search_console_domains =
+ if site.google_auth do
+ Plausible.Google.Api.fetch_verified_properties(site.google_auth)
+ end
+
+ imported_pageviews =
+ if site.imported_data do
+ Plausible.Stats.Clickhouse.imported_pageview_count(site)
+ else
+ 0
+ end
+
+ has_plugins_tokens? = Plausible.Plugins.API.Tokens.any?(site)
+
+ conn
+ |> render("settings_integrations.html",
+ site: site,
+ imported_pageviews: imported_pageviews,
+ has_plugins_tokens?: has_plugins_tokens?,
+ search_console_domains: search_console_domains,
+ dogfood_page_path: "/:dashboard/settings/integrations",
+ connect_live_socket: true,
+ layout: {PlausibleWeb.LayoutView, "site_settings.html"}
+ )
+ end
+
def update_google_auth(conn, %{"google_auth" => attrs}) do
site = conn.assigns[:site] |> Repo.preload(:google_auth)
@@ -316,7 +320,7 @@ defmodule PlausibleWeb.SiteController do
conn
|> put_flash(:success, "Google integration saved successfully")
- |> redirect(to: Routes.site_path(conn, :settings_search_console, site.domain))
+ |> redirect(to: Routes.site_path(conn, :settings_integrations, site.domain))
end
def delete_google_auth(conn, _params) do
@@ -328,19 +332,7 @@ defmodule PlausibleWeb.SiteController do
conn = put_flash(conn, :success, "Google account unlinked from Plausible")
- panel =
- conn.path_info
- |> List.last()
- |> String.split("-")
- |> List.last()
-
- case panel do
- "search" ->
- redirect(conn, to: Routes.site_path(conn, :settings_search_console, site.domain))
-
- "import" ->
- redirect(conn, to: Routes.site_path(conn, :settings_general, site.domain))
- end
+ redirect(conn, to: Routes.site_path(conn, :settings_integrations, site.domain))
end
def update_settings(conn, %{"site" => site_params}) do
@@ -862,7 +854,7 @@ defmodule PlausibleWeb.SiteController do
conn
|> put_flash(:success, "Import scheduled. An email will be sent when it completes.")
- |> redirect(to: Routes.site_path(conn, :settings_general, site.domain))
+ |> redirect(to: Routes.site_path(conn, :settings_integrations, site.domain))
end
def forget_imported(conn, _params) do
@@ -885,12 +877,12 @@ defmodule PlausibleWeb.SiteController do
conn
|> put_flash(:success, "Imported data has been cleared")
- |> redirect(to: Routes.site_path(conn, :settings_general, site.domain))
+ |> redirect(to: Routes.site_path(conn, :settings_integrations, site.domain))
true ->
conn
|> put_flash(:error, "No data has been imported")
- |> redirect(to: Routes.site_path(conn, :settings_general, site.domain))
+ |> redirect(to: Routes.site_path(conn, :settings_integrations, site.domain))
end
end
diff --git a/lib/plausible_web/live/components/form.ex b/lib/plausible_web/live/components/form.ex
index 1dd0a4fcf5..f7cbadb4b6 100644
--- a/lib/plausible_web/live/components/form.ex
+++ b/lib/plausible_web/live/components/form.ex
@@ -76,6 +76,40 @@ defmodule PlausibleWeb.Live.Components.Form do
"""
end
+ attr(:rest, :global)
+ attr(:id, :string, required: true)
+ attr(:class, :string, default: "")
+ attr(:name, :string, required: true)
+ attr(:label, :string, required: true)
+ attr(:value, :string, default: "")
+
+ def input_with_clipboard(assigns) do
+ ~H"""
+
+
+ <.input
+ id={@id}
+ name={@name}
+ label={@label}
+ value={@value}
+ type="text"
+ readonly="readonly"
+ class={[@class, "pr-20"]}
+ {@rest}
+ />
+
+ COPY
+
+
+
+ """
+ end
+
attr(:id, :any, default: nil)
attr(:label, :string, default: nil)
diff --git a/lib/plausible_web/live/plugins/api/settings.ex b/lib/plausible_web/live/plugins/api/settings.ex
new file mode 100644
index 0000000000..40c2b82802
--- /dev/null
+++ b/lib/plausible_web/live/plugins/api/settings.ex
@@ -0,0 +1,146 @@
+defmodule PlausibleWeb.Live.Plugins.API.Settings do
+ @moduledoc """
+ LiveView allowing listing, creating and revoking Plugins API tokens.
+ """
+ use Phoenix.LiveView
+ use Phoenix.HTML
+
+ alias Plausible.Sites
+ alias Plausible.Plugins.API.Tokens
+
+ def mount(
+ _params,
+ %{"domain" => domain, "current_user_id" => user_id} = session,
+ socket
+ ) do
+ socket =
+ socket
+ |> assign_new(:site, fn ->
+ Sites.get_for_user!(user_id, domain, [:owner, :admin, :super_admin])
+ end)
+ |> assign_new(:displayed_tokens, fn %{site: site} ->
+ Tokens.list(site)
+ end)
+
+ {:ok,
+ assign(socket,
+ domain: domain,
+ add_token?: not is_nil(session["new_token"]),
+ token_description: String.capitalize(session["new_token"] || ""),
+ current_user_id: user_id
+ )}
+ end
+
+ def render(assigns) do
+ ~H"""
+ <.live_component id="embedded_liveview_flash" module={PlausibleWeb.Live.Flash} flash={@flash} />
+
+ <%= if @add_token? do %>
+ <%= live_render(
+ @socket,
+ PlausibleWeb.Live.Plugins.API.TokenForm,
+ id: "token-form",
+ session: %{
+ "current_user_id" => @current_user_id,
+ "domain" => @domain,
+ "token_description" => @token_description,
+ "rendered_by" => self()
+ }
+ ) %>
+ <% end %>
+
+
+
+
+
+
+
+
+
+ Description
+
+
+ Hint
+
+
+ Revoke
+
+
+
+
+ <%= for token <- @displayed_tokens do %>
+
+
+ <%= token.description %>
+
+
+ **********<%= token.hint %>
+
+
+
+ Revoke
+
+
+
+ <% end %>
+
+
+
+
+ """
+ end
+
+ def handle_event("add-token", _params, socket) do
+ {:noreply, assign(socket, :add_token?, true)}
+ end
+
+ def handle_event("revoke-token", %{"token-id" => token_id}, socket) do
+ :ok = Tokens.delete(socket.assigns.site, token_id)
+ displayed_tokens = Enum.reject(socket.assigns.displayed_tokens, &(&1.id == token_id))
+ {:noreply, assign(socket, add_token?: false, displayed_tokens: displayed_tokens)}
+ end
+
+ def handle_info(:cancel_add_token, socket) do
+ {:noreply, assign(socket, add_token?: false)}
+ end
+
+ def handle_info({:token_added, token}, socket) do
+ displayed_tokens = [token | socket.assigns.displayed_tokens]
+
+ socket = put_flash(socket, :success, "Plugins API Token created successfully")
+
+ Process.send_after(self(), :clear_flash, 5000)
+
+ {:noreply,
+ assign(socket,
+ displayed_tokens: displayed_tokens,
+ add_token?: false,
+ token_description: ""
+ )}
+ end
+
+ def handle_info(:clear_flash, socket) do
+ {:noreply, clear_flash(socket)}
+ end
+end
diff --git a/lib/plausible_web/live/plugins/api/token_form.ex b/lib/plausible_web/live/plugins/api/token_form.ex
new file mode 100644
index 0000000000..0fd31815d3
--- /dev/null
+++ b/lib/plausible_web/live/plugins/api/token_form.ex
@@ -0,0 +1,114 @@
+defmodule PlausibleWeb.Live.Plugins.API.TokenForm do
+ @moduledoc """
+ Live view for the goal creation form
+ """
+ use Phoenix.LiveView
+ import PlausibleWeb.Live.Components.Form
+
+ alias Plausible.Repo
+ alias Plausible.Sites
+ alias Plausible.Plugins.API.{Token, Tokens}
+
+ def mount(
+ _params,
+ %{
+ "token_description" => token_description,
+ "current_user_id" => user_id,
+ "domain" => domain,
+ "rendered_by" => pid
+ },
+ socket
+ ) do
+ socket =
+ socket
+ |> assign_new(:site, fn ->
+ Sites.get_for_user!(user_id, domain, [:owner, :admin, :super_admin])
+ end)
+
+ token = Token.generate()
+ form = to_form(Token.insert_changeset(socket.assigns.site, token))
+
+ {:ok,
+ assign(socket,
+ token_description: token_description,
+ token: token,
+ current_user: Repo.get(Plausible.Auth.User, user_id),
+ form: form,
+ domain: domain,
+ rendered_by: pid,
+ tabs: %{custom_events: true, pageviews: false}
+ )}
+ end
+
+ def render(assigns) do
+ ~H"""
+
+
+
+
+ <.form
+ :let={f}
+ for={@form}
+ class="max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"
+ phx-submit="save-token"
+ phx-click-away="cancel-add-token"
+ >
+
Add Token for <%= @domain %>
+
+ <.input
+ autofocus
+ field={f[:description]}
+ label="Description"
+ class="focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-900 dark:text-gray-300 block w-7/12 rounded-md sm:text-sm border-gray-300 dark:border-gray-500 w-full p-2 mt-2"
+ placeholder="e.g. Signup"
+ value={@token_description}
+ autocomplete="off"
+ />
+
+ <.input_with_clipboard
+ id="token-clipboard"
+ name="token_clipboard"
+ label="API Token"
+ value={@token.raw}
+ onfocus="this.value = this.value;"
+ class="focus:ring-indigo-500 focus:border-indigo-500 bg-gray-50 dark:bg-gray-850 dark:text-gray-300 block w-7/12 rounded-md sm:text-sm border-gray-300 dark:border-gray-500 w-full p-2 mt-2"
+ />
+
+
+ Once created, we will not be able to show the Token again.
+ Please copy the Token now and store it in a secure place.
+
+ You'll need to paste it in the settings area of the Plausible WordPress plugin.
+
+
+
+
+ Add Token →
+
+
+
+
+
+ """
+ end
+
+ def handle_event("save-token", %{"token" => %{"description" => description}}, socket) do
+ case Tokens.create(socket.assigns.site, description, socket.assigns.token) do
+ {:ok, token, _} ->
+ send(socket.assigns.rendered_by, {:token_added, token})
+ {:noreply, socket}
+
+ {:error, changeset} ->
+ {:noreply, assign(socket, form: to_form(changeset))}
+ end
+ end
+
+ def handle_event("cancel-add-token", _value, socket) do
+ send(socket.assigns.rendered_by, :cancel_add_token)
+ {:noreply, socket}
+ end
+end
diff --git a/lib/plausible_web/router.ex b/lib/plausible_web/router.ex
index 67d9983358..9305f84a68 100644
--- a/lib/plausible_web/router.ex
+++ b/lib/plausible_web/router.ex
@@ -284,10 +284,10 @@ defmodule PlausibleWeb.Router do
get "/:website/settings/properties", SiteController, :settings_props
get "/:website/settings/funnels", SiteController, :settings_funnels
- get "/:website/settings/search-console", SiteController, :settings_search_console
get "/:website/settings/email-reports", SiteController, :settings_email_reports
get "/:website/settings/custom-domain", SiteController, :settings_custom_domain
get "/:website/settings/danger-zone", SiteController, :settings_danger_zone
+ get "/:website/settings/integrations", SiteController, :settings_integrations
put "/:website/settings/features/visibility/:setting",
SiteController,
diff --git a/lib/plausible_web/templates/site/settings_email_reports.html.eex b/lib/plausible_web/templates/site/settings_email_reports.html.heex
similarity index 57%
rename from lib/plausible_web/templates/site/settings_email_reports.html.eex
rename to lib/plausible_web/templates/site/settings_email_reports.html.heex
index 0423d6bb9e..de23058e04 100644
--- a/lib/plausible_web/templates/site/settings_email_reports.html.eex
+++ b/lib/plausible_web/templates/site/settings_email_reports.html.heex
@@ -1,20 +1,23 @@
Email Reports
- Send weekly/monthly analytics reports to as many addresses as you wish
- <%= link(to: "https://plausible.io/docs/email-reports", target: "_blank", rel: "noferrer") do %>
-
- <% end %>
+
+ Send weekly/monthly analytics reports to as many addresses as you wish
+
+
+
<%= if @weekly_report do %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/weekly-report/disable", method: :post, class: "bg-indigo-600 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring") do %>
-
+
+
<% end %>
<% else %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/weekly-report/enable", method: :post, class: "bg-gray-200 dark:bg-gray-700 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring") do %>
-
+
+
<% end %>
<% end %>
Send a weekly email report every Monday
@@ -25,13 +28,34 @@
<%= for recipient <- @weekly_report.recipients do %>
-
+
- <%= recipient %>
+
+ <%= recipient %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/weekly-report/recipients/#{recipient}", method: :delete) do %>
-
+
+
+
+
<% end %>
<% end %>
@@ -40,16 +64,35 @@
- <%= email_input f, :recipient, class: "focus:ring-indigo-500 dark:bg-gray-900 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300 dark:border-gray-500 dark:placeholder-gray-400 dark:text-gray-100", placeholder: "recipient@example.com", required: "true" %>
+ <%= email_input(f, :recipient,
+ class:
+ "focus:ring-indigo-500 dark:bg-gray-900 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300 dark:border-gray-500 dark:placeholder-gray-400 dark:text-gray-100",
+ placeholder: "recipient@example.com",
+ required: "true"
+ ) %>
<%= submit class: "-ml-px relative button rounded-l-none" do %>
-
+
+
+
+
Add recipient
<% end %>
@@ -61,11 +104,13 @@
<%= if @monthly_report do %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/monthly-report/disable", method: :post, class: "bg-indigo-600 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring") do %>
-
+
+
<% end %>
<% else %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/monthly-report/enable", method: :post, class: "bg-gray-200 dark:bg-gray-700 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring") do %>
-
+
+
<% end %>
<% end %>
Send a monthly email report on 1st of the month
@@ -76,13 +121,34 @@
<%= for recipient <- @monthly_report.recipients do %>
-
+
- <%= recipient %>
+
+ <%= recipient %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/monthly-report/recipients/#{recipient}", method: :delete) do %>
-
+
+
+
+
<% end %>
<% end %>
@@ -91,16 +157,35 @@
- <%= email_input f, :recipient, class: "focus:ring-indigo-500 dark:bg-gray-900 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300 dark:border-gray-500 dark:placeholder-gray-400 dark:text-gray-100", placeholder: "recipient@example.com", required: "true" %>
+ <%= email_input(f, :recipient,
+ class:
+ "focus:ring-indigo-500 dark:bg-gray-900 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300 dark:border-gray-500 dark:placeholder-gray-400 dark:text-gray-100",
+ placeholder: "recipient@example.com",
+ required: "true"
+ ) %>
<%= submit class: "-ml-px relative button rounded-l-none" do %>
-
+
+
+
+
Add recipient
<% end %>
@@ -112,21 +197,26 @@
- Traffic Spike Notifications
- Get notified when your site has unusually high number of current visitors
- <%= link(to: "https://plausible.io/docs/traffic-spikes", target: "_blank", rel: "noreferrer") do %>
-
- <% end %>
+
+ Traffic Spike Notifications
+
+
+ Get notified when your site has unusually high number of current visitors
+
+
+
<%= if @spike_notification do %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/spike-notification/disable", method: :post, class: "bg-indigo-600 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring") do %>
-
+
+
<% end %>
<% else %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/spike-notification/enable", method: :post, class: "bg-gray-200 dark:bg-gray-700 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring") do %>
-
+
+
<% end %>
<% end %>
Send notifications of traffic spikes
@@ -134,35 +224,64 @@
<%= if @spike_notification do %>
-
<%= form_for Plausible.Site.SpikeNotification.changeset(@spike_notification, %{}), "/sites/#{URI.encode_www_form(@site.domain)}/spike-notification", fn f -> %>
Current visitor threshold
- <%= number_input f, :threshold, class: "focus:ring-indigo-500 dark:bg-gray-900 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300 dark:border-gray-500 dark:text-gray-100" %>
+ <%= number_input(f, :threshold,
+ class:
+ "focus:ring-indigo-500 dark:bg-gray-900 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300 dark:border-gray-500 dark:text-gray-100"
+ ) %>
Save threshold
- <% end %>
+ <% end %>
Notification recipients
<%= for recipient <- @spike_notification.recipients do %>
-
+
- <%= recipient %>
+
+ <%= recipient %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/spike-notification/recipients/#{recipient}", method: :delete) do %>
-
+
+
+
+
<% end %>
<% end %>
@@ -171,16 +290,35 @@
- <%= email_input f, :recipient, class: "focus:ring-indigo-500 dark:bg-gray-900 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300 dark:border-gray-500 dark:placeholder-gray-400 dark:text-gray-100", placeholder: "recipient@example.com", required: "true" %>
+ <%= email_input(f, :recipient,
+ class:
+ "focus:ring-indigo-500 dark:bg-gray-900 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300 dark:border-gray-500 dark:placeholder-gray-400 dark:text-gray-100",
+ placeholder: "recipient@example.com",
+ required: "true"
+ ) %>
<%= submit class: "-ml-px relative button rounded-l-none" do %>
-
+
+
+
+
Add recipient
<% end %>
diff --git a/lib/plausible_web/templates/site/settings_funnels.html.heex b/lib/plausible_web/templates/site/settings_funnels.html.heex
index 377a147b4b..49bb2dd82d 100644
--- a/lib/plausible_web/templates/site/settings_funnels.html.heex
+++ b/lib/plausible_web/templates/site/settings_funnels.html.heex
@@ -12,21 +12,7 @@
Compose Goals into Funnels
- <%= link(to: "https://plausible.io/docs/funnel-analysis", target: "_blank", rel: "noreferrer") do %>
-
-
-
-
- <% end %>
+
-
-
-
-
- <%= label nil, "Domain", class: "block text-sm font-medium leading-5 text-gray-700 dark:text-gray-300" %>
- <%= text_input nil, :domain, value: @site.domain, disabled: "disabled", class: "dark:bg-gray-900 w-full mt-1 block pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-500 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md dark:text-gray-100 text-gray-500" %>
-
-
-
- <%= link "Change Domain", to: Routes.site_path(@conn, :change_domain, @site.domain), class: "button" %>
-
-
-
-
-<%= form_for @changeset, "/#{URI.encode_www_form(@site.domain)}/settings", fn f -> %>
-
-
-
-
-
- <%= label f, :timezone, "Reporting Timezone", class: "block text-sm font-medium leading-5 text-gray-700 dark:text-gray-300" %>
- <%= select f, :timezone, Plausible.Timezones.options(), class: "dark:bg-gray-900 mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-500 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md dark:text-gray-100 cursor-pointer" %>
-
-
-
- <%= submit "Save", class: "button" %>
-
-
-
-<% end %>
-
-<%= form_for @conn, "/", [class: "shadow bg-white dark:bg-gray-800 sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6"], fn f -> %>
-
-
-
-
-
<%= textarea f, :domain, id: "snippet_code", class: "transition overflow-hidden bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 pr-6 text-gray-700 dark:text-gray-300 leading-normal focus:outline-none focus:bg-white focus:border-gray-300 dark:focus:border-gray-500 text-xs mt-2 resize-none", value: render_snippet(@site), rows: 2 %>
-
-
-
-
-
-<% end %>
-
-
-
-
- <%= if Keyword.get(Application.get_env(:plausible, :google), :client_id) do %>
- <%= cond do %>
- <% @site.imported_data && @site.imported_data.status == "importing" -> %>
-
-
-
- Import from <%= @site.imported_data.source %>
-
-
-
-
-
-
- From <%= PlausibleWeb.EmailView.date_format(@site.imported_data.start_date) %> to <%= PlausibleWeb.EmailView.date_format(@site.imported_data.end_date) %>
-
-
- <%= link("Cancel import", to: "/#{URI.encode_www_form(@site.domain)}/settings/forget-imported", method: :delete, class: "inline-block mt-4 px-4 py-2 border border-gray-300 dark:border-gray-500 text-sm leading-5 font-medium rounded-md text-red-700 bg-white dark:bg-gray-800 hover:text-red-500 dark:hover:text-red-400 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150") %>
-
-
- <% @site.imported_data && @site.imported_data.status == "ok" -> %>
-
-
-
- Import from <%= @site.imported_data.source %>
-
-
-
-
-
- From <%= PlausibleWeb.EmailView.date_format(@site.imported_data.start_date) %> to <%= PlausibleWeb.EmailView.date_format(@site.imported_data.end_date) %>
-
-
- <%= link("Clear " <> PlausibleWeb.StatsView.large_number_format(@imported_pageviews) <> " Imported Pageviews", to: "/#{URI.encode_www_form(@site.domain)}/settings/forget-imported", method: :delete, class: "inline-block mt-4 px-4 py-2 border border-gray-300 dark:border-gray-500 text-sm leading-5 font-medium rounded-md text-red-700 bg-white dark:bg-gray-800 hover:text-red-500 dark:hover:text-red-400 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150") %>
-
-
- <% true -> %>
- <%= if @site.imported_data && @site.imported_data.status == "error" do %>
-
Your latest import has failed. You can try importing again by clicking the button below. If you try multiple times and the import keeps failing, please contact support.
- <% end %>
-
- <%= button(to: Plausible.Google.Api.import_authorize_url(@site.id, "import"), class: "inline-flex pr-4 items-center border border-gray-100 shadow rounded-md focus:outline-none focus:ring-1 focus:ring-offset-1 focus:ring-gray-200 mt-8 hover:bg-gray-50 dark:hover:bg-gray-700") do %>
- <%= google_logo() %>
- Continue with Google
- <% end %>
-
-
- <% end %>
- <% else %>
-
-
-
An extra step is needed to set up your Plausible Analytics Self Hosted for the Google Search Console integration.
- Find instructions <%= link("here", to: "https://plausible.io/docs/self-hosting-configuration#google-search-integration", class: "text-indigo-500") %>
-
- <% end %>
-
diff --git a/lib/plausible_web/templates/site/settings_general.html.heex b/lib/plausible_web/templates/site/settings_general.html.heex
new file mode 100644
index 0000000000..1ef45e133f
--- /dev/null
+++ b/lib/plausible_web/templates/site/settings_general.html.heex
@@ -0,0 +1,111 @@
+
+
+
+
+
+ <%= label(nil, "Domain",
+ class: "block text-sm font-medium leading-5 text-gray-700 dark:text-gray-300"
+ ) %>
+ <%= text_input(nil, :domain,
+ value: @site.domain,
+ disabled: "disabled",
+ class:
+ "dark:bg-gray-900 w-full mt-1 block pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-500 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md dark:text-gray-100 text-gray-500"
+ ) %>
+
+
+
+ <%= link("Change Domain",
+ to: Routes.site_path(@conn, :change_domain, @site.domain),
+ class: "button"
+ ) %>
+
+
+
+
+<%= form_for @changeset, "/#{URI.encode_www_form(@site.domain)}/settings", fn f -> %>
+
+
+
+
+
+ <%= label(f, :timezone, "Reporting Timezone",
+ class: "block text-sm font-medium leading-5 text-gray-700 dark:text-gray-300"
+ ) %>
+ <%= select(f, :timezone, Plausible.Timezones.options(),
+ class:
+ "dark:bg-gray-900 mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-500 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md dark:text-gray-100 cursor-pointer"
+ ) %>
+
+
+
+ <%= submit("Save", class: "button") %>
+
+
+
+<% end %>
+
+<%= form_for @conn, "/", [class: "shadow bg-white dark:bg-gray-800 sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6"], fn f -> %>
+
+
+
+
+
+ <%= textarea(f, :domain,
+ id: "snippet_code",
+ class:
+ "transition overflow-hidden bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 pr-6 text-gray-700 dark:text-gray-300 leading-normal focus:outline-none focus:bg-white focus:border-gray-300 dark:focus:border-gray-500 text-xs mt-2 resize-none",
+ value: render_snippet(@site),
+ rows: 2
+ ) %>
+
+
+
+
+
+
+
+
+
+<% end %>
diff --git a/lib/plausible_web/templates/site/settings_goals.html.heex b/lib/plausible_web/templates/site/settings_goals.html.heex
index 191ae29e1f..d987e95c06 100644
--- a/lib/plausible_web/templates/site/settings_goals.html.heex
+++ b/lib/plausible_web/templates/site/settings_goals.html.heex
@@ -11,21 +11,7 @@
>compose Goals into Funnels.
- <%= link(to: "https://plausible.io/docs/goal-conversions", target: "_blank", rel: "noreferrer") do %>
-
-
-
-
- <% end %>
+
+
+
+ <%= if Keyword.get(Application.get_env(:plausible, :google), :client_id) do %>
+ <%= cond do %>
+ <% @site.imported_data && @site.imported_data.status == "importing" -> %>
+
+
+
+ Import from <%= @site.imported_data.source %>
+
+
+
+
+
+
+
+
+ From <%= PlausibleWeb.EmailView.date_format(@site.imported_data.start_date) %> to <%= PlausibleWeb.EmailView.date_format(
+ @site.imported_data.end_date
+ ) %>
+
+
+ <%= link("Cancel import",
+ to: "/#{URI.encode_www_form(@site.domain)}/settings/forget-imported",
+ method: :delete,
+ class:
+ "inline-block mt-4 px-4 py-2 border border-gray-300 dark:border-gray-500 text-sm leading-5 font-medium rounded-md text-red-700 bg-white dark:bg-gray-800 hover:text-red-500 dark:hover:text-red-400 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150"
+ ) %>
+
+ <% @site.imported_data && @site.imported_data.status == "ok" -> %>
+
+
+
+ Import from <%= @site.imported_data.source %>
+
+
+
+
+
+ From <%= PlausibleWeb.EmailView.date_format(@site.imported_data.start_date) %> to <%= PlausibleWeb.EmailView.date_format(
+ @site.imported_data.end_date
+ ) %>
+
+
+ <%= link(
+ "Clear " <>
+ PlausibleWeb.StatsView.large_number_format(@imported_pageviews) <>
+ " Imported Pageviews",
+ to: "/#{URI.encode_www_form(@site.domain)}/settings/forget-imported",
+ method: :delete,
+ class:
+ "inline-block mt-4 px-4 py-2 text-sm leading-5 font-medium text-red-600 bg-white dark:bg-gray-800 hover:text-red-500 dark:hover:text-red-400 focus:outline-none focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150"
+ ) %>
+
+ <% true -> %>
+ <%= if @site.imported_data && @site.imported_data.status == "error" do %>
+
+ Your latest import has failed. You can try importing again by clicking the button below. If you try multiple times and the import keeps failing, please contact support.
+
+ <% end %>
+
+ <% end %>
+ <% else %>
+
+
+
+
+
+
+ An extra step is needed to set up your Plausible Analytics Self Hosted for the Google Search Console integration.
+ Find instructions <%= link("here",
+ to: "https://plausible.io/docs/self-hosting-configuration#google-search-integration",
+ class: "text-indigo-500"
+ ) %>
+
+
+ <% end %>
+
diff --git a/lib/plausible_web/templates/site/settings_integrations.html.heex b/lib/plausible_web/templates/site/settings_integrations.html.heex
new file mode 100644
index 0000000000..59acd3b4ea
--- /dev/null
+++ b/lib/plausible_web/templates/site/settings_integrations.html.heex
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+ <%= live_render(@conn, PlausibleWeb.Live.Plugins.API.Settings,
+ session: %{
+ "site_id" => @site.id,
+ "domain" => @site.domain,
+ "new_token" => @conn.query_params["new_token"]
+ }
+ ) %>
+
+
diff --git a/lib/plausible_web/templates/site/settings_people.html.eex b/lib/plausible_web/templates/site/settings_people.html.heex
similarity index 52%
rename from lib/plausible_web/templates/site/settings_people.html.eex
rename to lib/plausible_web/templates/site/settings_people.html.heex
index adc5bcd095..5d57dd6ba2 100644
--- a/lib/plausible_web/templates/site/settings_people.html.eex
+++ b/lib/plausible_web/templates/site/settings_people.html.heex
@@ -1,10 +1,11 @@
@@ -12,7 +13,9 @@
- <%= img_tag(Plausible.Auth.User.profile_img_url(membership.user), class: "h-8 w-8 rounded-full") %>
+ <%= img_tag(Plausible.Auth.User.profile_img_url(membership.user),
+ class: "h-8 w-8 rounded-full"
+ ) %>
-
+
<%= membership.role |> Atom.to_string() |> String.capitalize() %>
-
+
+
+
+