Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import 'abortcontroller-polyfill/dist/polyfill-patch-fetch'
import Alpine from 'alpinejs'
import './liveview/live_socket'
import comboBox from './liveview/combo-box'
import copySnippet from './liveview/copy-snippet'
import dropdown from './liveview/dropdown'
import './liveview/phx_events'

Alpine.data('dropdown', dropdown)
Alpine.data('comboBox', comboBox)
Alpine.data('copySnippet', copySnippet)
Alpine.start()

if (document.querySelectorAll('[data-modal]').length > 0) {
Expand Down
34 changes: 34 additions & 0 deletions assets/js/liveview/copy-snippet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copy-to-clipboard behaviour for a readonly textarea holding a code snippet.
// Expects an `x-ref="snippet"` textarea within the same `x-data` scope.

const RESET_DELAY = 2000

const hasSelection = (el) => el.selectionStart !== el.selectionEnd

const isFullySelected = (el) =>
el.selectionStart === 0 && el.selectionEnd === el.value.length

export default () => ({
copied: false,
copyAll() {
const el = this.$refs.snippet
el.focus()
el.select()
document.execCommand('copy')
this.copied = true

setTimeout(() => {
this.copied = false

// Leave it alone if the user has highlighted their own text since.
if (isFullySelected(el)) {
el.setSelectionRange(0, 0)
el.blur()
}
}, RESET_DELAY)
},
copyIfNoSelection() {
if (hasSelection(this.$refs.snippet)) return
this.copyAll()
}
})
13 changes: 4 additions & 9 deletions e2e/tests/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,7 @@ export async function register({

await page.getByRole('button', { name: 'Activate' }).click()

await expect(
page.getByRole('button', { name: 'Install Plausible' })
).toBeVisible()
await expect(page.getByRole('button', { name: 'Add site' })).toBeVisible()
}

export async function login({ page, user }: { page: Page; user: User }) {
Expand Down Expand Up @@ -149,19 +147,16 @@ export async function addSite({
}) {
await page.goto('/sites/new', { waitUntil: 'commit' })

await expect(
page.getByRole('button', { name: 'Install Plausible' })
).toBeVisible()
await expect(page.getByRole('button', { name: 'Add site' })).toBeVisible()

await page.getByLabel('Domain').fill(domain)
await page.getByLabel('Reporting timezone').selectOption('Etc/UTC')

await page.getByRole('button', { name: 'Install Plausible' }).click()
await page.getByRole('button', { name: 'Add site' }).click()

await expect(page).toHaveURL(/\/installation/)

await expect(
page.getByRole('button', { name: /Verify .* installation/ })
page.getByRole('button', { name: "I've installed it" })
).toBeVisible()
}

Expand Down
11 changes: 4 additions & 7 deletions lib/plausible/site.ex
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,9 @@ defmodule Plausible.Site do
end

on_ee do
@domain_unique_error """
This domain cannot be registered. Perhaps one of your colleagues registered it? If that's not the case, please contact support@plausible.io
"""
@domain_unique_error "This domain is already registered. Ask the owner for access, or contact support@plausible.io"
else
@domain_unique_error """
This domain cannot be registered. Perhaps one of your colleagues registered it?
"""
@domain_unique_error "This domain is already registered. Ask the owner for access."
end

on_ee do
Expand All @@ -116,7 +112,8 @@ defmodule Plausible.Site do
site
|> cast(attrs, @changeset_cast_fields)
|> clean_domain()
|> validate_required([:domain, :timezone])
|> validate_required([:domain], message: "Please enter a domain or subdomain")
|> validate_required([:timezone])
|> validate_timezone()
|> validate_domain_format()
|> validate_domain_reserved_characters()
Expand Down
63 changes: 21 additions & 42 deletions lib/plausible_web/components/flow_progress.ex
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
defmodule PlausibleWeb.Components.FlowProgress do
@moduledoc """
Component for provisioning/registration flows displaying
progress status. See `PlausibleWeb.Flows` for the list of
flow definitions.
Dotted progress indicator shown during the registration flow.
One small dot per step in `PlausibleWeb.Flows.steps/1`, with completed
and current steps highlighted.
"""
use Phoenix.Component

Expand All @@ -20,45 +20,24 @@ defmodule PlausibleWeb.Components.FlowProgress do
)

~H"""
<div :if={not Enum.empty?(@steps)} class="mt-6 hidden md:block" id="flow-progress">
<div class="flex items-center justify-between max-w-4xl mx-auto my-8">
<%= for {step, idx} <- Enum.with_index(@steps) do %>
<div class="flex items-center text-base">
<div
:if={idx < @current_step_idx}
class="size-6 bg-green-500 dark:bg-green-600 text-white rounded-full flex items-center justify-center"
>
<Heroicons.check class="size-4" />
</div>
<div
:if={idx == @current_step_idx}
class="size-6 bg-indigo-600 text-xs text-white font-bold rounded-full flex items-center justify-center"
>
{idx + 1}
</div>
<div
:if={idx > @current_step_idx}
class="size-6 bg-gray-300 text-xs text-white font-bold dark:bg-gray-800 rounded-full flex items-center justify-center"
>
{idx + 1}
</div>
<span :if={idx < @current_step_idx} class="ml-2 text-gray-500">
{step}
</span>
<span
:if={idx == @current_step_idx}
class="ml-2 font-semibold text-black dark:text-gray-300"
>
{step}
</span>
<span :if={idx > @current_step_idx} class="ml-2 text-gray-500">
{step}
</span>
</div>
<div :if={idx + 1 != length(@steps)} class="flex-1 h-px bg-gray-300 mx-4 dark:bg-gray-800 ">
</div>
<% end %>
</div>
<div
:if={not Enum.empty?(@steps)}
id="flow-progress"
class="flex items-center gap-2"
aria-label="Progress"
>
<span
:for={{step, idx} <- Enum.with_index(@steps)}
class={
cond do
idx == @current_step_idx -> "h-2 w-5 rounded-full bg-indigo-600 dark:bg-gray-100"
idx < @current_step_idx -> "size-2 rounded-full bg-indigo-600 dark:bg-gray-100"
true -> "size-2 rounded-full bg-gray-300 dark:bg-gray-600"
end
}
aria-current={idx == @current_step_idx && "step"}
aria-label={step}
/>
</div>
"""
end
Expand Down
83 changes: 32 additions & 51 deletions lib/plausible_web/components/generic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ defmodule PlausibleWeb.Components.Generic do
@button_base_class "btn-base"

@button_sizes %{
"xs" => "btn-xs",
"sm" => "btn-sm",
"md" => "btn-md"
}
Expand Down Expand Up @@ -477,20 +478,32 @@ defmodule PlausibleWeb.Components.Generic do
def spinner(assigns) do
~H"""
<svg
class={["animate-spin h-4 w-4 text-indigo-500", @class]}
class={["animate-spin size-4 text-indigo-500", @class]}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
fill="none"
{@rest}
>
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4">
</circle>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
>
</path>
<circle
class="opacity-20"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
stroke-linecap="round"
/>
<circle
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
stroke-linecap="round"
pathLength="100"
stroke-dasharray="30 70"
transform="rotate(-90 12 12)"
/>
</svg>
"""
end
Expand Down Expand Up @@ -545,31 +558,31 @@ defmodule PlausibleWeb.Components.Generic do
<span
:if={@server_mode?}
class={[
"relative inline-flex h-6 w-11 shrink-0 rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-hidden focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2",
"relative inline-flex h-5 w-9 shrink-0 rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-hidden focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2",
if(@checked, do: "bg-indigo-600", else: "dark:bg-gray-600 bg-gray-200"),
if(@disabled, do: "opacity-50")
]}
>
<span
aria-hidden="true"
class={[
"pointer-events-none inline-block size-5 transform rounded-full bg-white shadow-sm ring-0 transition duration-200 ease-in-out",
if(@checked, do: "dark:bg-white translate-x-5", else: "dark:bg-white translate-x-0")
"pointer-events-none inline-block size-4 transform rounded-full bg-white shadow-sm ring-0 transition duration-200 ease-in-out",
if(@checked, do: "dark:bg-white translate-x-4", else: "dark:bg-white translate-x-0")
]}
/>
</span>
<span
:if={!@server_mode?}
class={[
"relative inline-flex h-6 w-11 shrink-0 rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-hidden focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2",
"relative inline-flex h-5 w-9 shrink-0 rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-hidden focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2",
if(@disabled, do: "opacity-50")
]}
x-bind:class={"#{@js_active_var} ? 'bg-indigo-600' : 'dark:bg-gray-600 bg-gray-200'"}
>
<span
aria-hidden="true"
class="pointer-events-none inline-block size-5 transform rounded-full bg-white shadow-sm ring-0 transition duration-200 ease-in-out"
x-bind:class={"#{@js_active_var} ? 'dark:bg-white translate-x-5' : 'dark:bg-white translate-x-0'"}
class="pointer-events-none inline-block size-4 transform rounded-full bg-white shadow-sm ring-0 transition duration-200 ease-in-out"
x-bind:class={"#{@js_active_var} ? 'dark:bg-white translate-x-4' : 'dark:bg-white translate-x-0'"}
/>
</span>
</button>
Expand Down Expand Up @@ -870,38 +883,6 @@ defmodule PlausibleWeb.Components.Generic do
"""
end

attr :id, :string, required: true
attr :text, :string, required: true
attr :rows, :integer, required: true
attr :resizable, :boolean, default: false

def copyable_readonly_text_area(assigns) do
~H"""
<div class={["relative w-full"]}>
<textarea
id={@id}
class={[
"w-full bg-transparent border-1 border-gray-300 rounded-md p-4 text-sm text-gray-700 dark:border-gray-750 dark:text-gray-300",
if(!@resizable, do: "resize-none")
]}
rows={@rows}
readonly
><%= @text %></textarea>

<a
onclick={"var input = document.getElementById('#{@id}'); input.focus(); input.select(); document.execCommand('copy'); event.stopPropagation();"}
href="javascript:void(0)"
class="absolute flex items-center text-xs font-medium text-indigo-600 no-underline bottom-2 right-4 p-2 transition-colors duration-150 hover:text-indigo-700 dark:text-indigo-500 dark:hover:text-indigo-400"
>
<Heroicons.document_duplicate class="pr-1 text-current size-5" />
<span>
COPY
</span>
</a>
</div>
"""
end

slot :title
slot :subtitle
slot :inner_block, required: true
Expand Down Expand Up @@ -1055,7 +1036,7 @@ defmodule PlausibleWeb.Components.Generic do
<button
type="submit"
class={[
"relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full transition-colors ease-in-out duration-200",
"relative inline-flex flex-shrink-0 h-5 w-9 border-2 border-transparent rounded-full transition-colors ease-in-out duration-200",
if(@set_to, do: "bg-indigo-600", else: "bg-gray-200 dark:bg-gray-600"),
if(@disabled?, do: "cursor-not-allowed")
]}
Expand All @@ -1064,8 +1045,8 @@ defmodule PlausibleWeb.Components.Generic do
<span
aria-hidden="true"
class={[
"inline-block size-5 rounded-full bg-white shadow transform transition ease-in-out duration-200",
if(@set_to, do: "translate-x-5", else: "translate-x-0")
"inline-block size-4 rounded-full bg-white shadow transform transition ease-in-out duration-200",
if(@set_to, do: "translate-x-4", else: "translate-x-0")
]}
/>
</button>
Expand Down
19 changes: 19 additions & 0 deletions lib/plausible_web/components/icons.ex
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,25 @@ defmodule PlausibleWeb.Components.Icons do

attr :class, :any, default: []

def copy_icon(assigns) do
~H"""
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18" class={@class}>
<g
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
>
<path d="M2.25 6.75v6.5a2 2 0 0 0 2 2h7.5" />
<path d="M7.25 12.25h6.5a2 2 0 0 0 2-2v-5.5a2 2 0 0 0-2-2h-6.5a2 2 0 0 0-2 2v5.5a2 2 0 0 0 2 2" />
</g>
</svg>
"""
end

attr :class, :any, default: []

def envelope_icon(assigns) do
~H"""
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class={@class}>
Expand Down
6 changes: 3 additions & 3 deletions lib/plausible_web/components/site/toggle_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ defmodule PlausibleWeb.Components.Site.Feature.ToggleLive do
phx-click="toggle"
phx-target={@myself}
class={[
"relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full transition-colors ease-in-out duration-200",
"relative inline-flex flex-shrink-0 h-5 w-9 border-2 border-transparent rounded-full transition-colors ease-in-out duration-200",
if(@current_setting, do: "bg-indigo-600", else: "bg-gray-200 dark:bg-gray-600"),
if(@disabled?, do: "cursor-not-allowed")
]}
Expand All @@ -42,8 +42,8 @@ defmodule PlausibleWeb.Components.Site.Feature.ToggleLive do
<span
aria-hidden="true"
class={[
"inline-block size-5 rounded-full bg-white shadow transform transition ease-in-out duration-200",
if(@current_setting, do: "translate-x-5", else: "translate-x-0")
"inline-block size-4 rounded-full bg-white shadow transform transition ease-in-out duration-200",
if(@current_setting, do: "translate-x-4", else: "translate-x-0")
]}
/>
</button>
Expand Down
Loading
Loading