First pass: unify onboarding UI (#4445)

* First pass: unify onboarding UI

* Dark mode fixes

* Format

* Update self-hosted index

* Remove unnecessary child box in Enter Your 2FA screen

* Fixup Enter Your 2FA screen
This commit is contained in:
hq1 2024-08-19 10:10:54 +03:00 committed by GitHub
parent 3b485afc91
commit 572f8abac5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 677 additions and 574 deletions

View File

@ -27,7 +27,7 @@ defmodule PlausibleWeb.Components.Generic do
"border border-gray-300 dark:border-gray-500 text-red-700 bg-white dark:bg-gray-800 hover:text-red-500 dark:hover:text-red-400 focus:border-blue-300 active:text-red-800"
}
@button_base_class "inline-flex items-center justify-center gap-x-2 rounded-md px-3.5 py-2.5 text-sm font-semibold shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 disabled:bg-gray-400 dark:disabled:text-white dark:disabled:text-gray-400 dark:disabled:bg-gray-700"
@button_base_class "inline-flex items-center justify-center gap-x-2 rounded-md px-3.5 py-2.5 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 disabled:bg-gray-400 dark:disabled:text-white dark:disabled:text-gray-400 dark:disabled:bg-gray-700"
attr(:type, :string, default: "button")
attr(:theme, :string, default: "primary")
@ -368,4 +368,46 @@ defmodule PlausibleWeb.Components.Generic do
["w-4 h-4"]
end
end
slot :title
slot :subtitle
slot :inner_block, required: true
slot :footer
attr :outer_markup, :boolean, default: true
def focus_box(assigns) do
~H"""
<div class={[
"bg-white w-full max-w-lg mx-auto dark:bg-gray-800 text-black dark:text-gray-100",
@outer_markup && "shadow-md rounded mb-4 mt-8"
]}>
<div class={[@outer_markup && "p-8"]}>
<h2 :if={@title != []} class="text-xl font-black dark:text-gray-100">
<%= render_slot(@title) %>
</h2>
<div :if={@subtitle != []} class="mt-2 dark:text-gray-200">
<%= render_slot(@subtitle) %>
</div>
<div :if={@title != []} class="mt-8">
<%= render_slot(@inner_block) %>
</div>
<div :if={@title == []}>
<%= render_slot(@inner_block) %>
</div>
</div>
<div
:if={@footer != []}
class="flex flex-col dark:text-gray-200 border-t border-gray-300 dark:border-gray-700"
>
<div class={[@outer_markup && "p-8"]}>
<%= render_slot(@footer) %>
</div>
</div>
</div>
"""
end
end

View File

@ -21,104 +21,103 @@ defmodule PlausibleWeb.Live.Components.Verification do
def render(assigns) do
~H"""
<div
class={[
"dark:text-gray-100 text-center bg-white dark:bg-gray-800 flex flex-col",
if(not @modal?, do: "shadow-md rounded px-8 pt-6 pb-4 mb-4 mt-4 h-96", else: "h-72")
]}
id="progress-indicator"
>
<div
:if={not @finished? or (not @modal? and @success?)}
class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100 dark:bg-gray-700"
>
<div class="block pulsating-circle"></div>
</div>
<div
:if={@finished? and @success? and @modal?}
class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100 dark:bg-green-500"
id="check-circle"
>
<Heroicons.check_badge class="h-6 w-6 text-green-600 bg-green-100 dark:bg-green-500 dark:text-green-200" />
</div>
<div
:if={@finished? and not @success?}
class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-red-100 dark:bg-red-200"
id="error-circle"
>
<Heroicons.exclamation_triangle class="h-6 w-6 text-red-600 bg-red-100 dark:bg-red-200 dark:text-red-800" />
</div>
<div class="mt-6">
<h3 class="font-semibold leading-6 text-xl">
<span :if={@finished? and @success?}>Success!</span>
<span :if={not @finished?}>Verifying your integration</span>
<span :if={@finished? and not @success? and @interpretation}>
<%= List.first(@interpretation.errors) %>
</span>
</h3>
<p :if={@finished? and @success? and @modal?} id="progress" class="mt-2">
Your integration is working and visitors are being counted accurately
</p>
<p :if={@finished? and @success? and not @modal?} id="progress" class="mt-2 animate-pulse">
Your integration is working. Awaiting your first pageview.
</p>
<p :if={not @finished?} class="mt-2 animate-pulse" id="progress"><%= @message %></p>
<p
:if={@finished? and not @success? and @interpretation}
class="mt-2 text-ellipsis overflow-hidden"
id="recommendation"
<div id="progress-indicator">
<PlausibleWeb.Components.Generic.focus_box outer_markup={not @modal?}>
<div
:if={not @finished? or (not @modal? and @success?)}
class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100 dark:bg-gray-700"
>
<span><%= List.first(@interpretation.recommendations).text %>.&nbsp;</span>
<.styled_link href={List.first(@interpretation.recommendations).url} new_tab={true}>
Learn more
</.styled_link>
</p>
</div>
<div class="block pulsating-circle"></div>
</div>
<div :if={@finished?} class="mt-auto">
<.button_link :if={not @success?} href="#" phx-click="retry" class="font-bold w-full">
Verify integration again
</.button_link>
<.button_link
:if={@success?}
href={"/#{URI.encode_www_form(@domain)}?skip_to_dashboard=true"}
class="w-full font-bold mb-4"
<div
:if={@finished? and @success? and @modal?}
class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100 dark:bg-green-500"
id="check-circle"
>
Go to the dashboard
</.button_link>
</div>
<Heroicons.check_badge class="h-6 w-6 text-green-600 bg-green-100 dark:bg-green-500 dark:text-green-200" />
</div>
<div
:if={
<div
:if={@finished? and not @success?}
class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-red-100 dark:bg-red-200"
id="error-circle"
>
<Heroicons.exclamation_triangle class="h-6 w-6 text-red-600 bg-red-100 dark:bg-red-200 dark:text-red-800" />
</div>
<div class="mt-6">
<h3 class="font-semibold leading-6 text-xl">
<span :if={@finished? and @success?}>Success!</span>
<span :if={not @finished?}>Verifying your integration</span>
<span :if={@finished? and not @success? and @interpretation}>
<%= List.first(@interpretation.errors) %>
</span>
</h3>
<p :if={@finished? and @success? and @modal?} id="progress" class="mt-2">
Your integration is working and visitors are being counted accurately
</p>
<p :if={@finished? and @success? and not @modal?} id="progress" class="mt-2 animate-pulse">
Your integration is working. Awaiting your first pageview.
</p>
<p :if={not @finished?} class="mt-2 animate-pulse" id="progress"><%= @message %></p>
<p
:if={@finished? and not @success? and @interpretation}
class="mt-2 text-ellipsis overflow-hidden"
id="recommendation"
>
<span><%= List.first(@interpretation.recommendations).text %>.&nbsp;</span>
<.styled_link href={List.first(@interpretation.recommendations).url} new_tab={true}>
Learn more
</.styled_link>
</p>
</div>
<div :if={@finished?} class="mt-8">
<.button_link :if={not @success?} href="#" phx-click="retry" class="w-full">
Verify integration again
</.button_link>
<.button_link
:if={@success?}
href={"/#{URI.encode_www_form(@domain)}?skip_to_dashboard=true"}
class="w-full font-bold mb-4"
>
Go to the dashboard
</.button_link>
</div>
<:footer :if={
(not @modal? and not @success?) or
(@finished? and not @success?)
}
class="mt-auto text-sm"
>
<%= if ee?() and @finished? and not @success? and @attempts >= 3 do %>
Need further help with your integration? Do
<.styled_link href="https://plausible.io/contact">
contact us
</.styled_link>
<br />
<% end %>
<%= if not @success? and not @modal? do %>
Need to see the snippet again?
<.styled_link href={"/#{URI.encode_www_form(@domain)}/snippet?flow=#{@flow}"}>
Click here
</.styled_link>
<br /> Run verification later and go to Site Settings?
<.styled_link href={"/#{URI.encode_www_form(@domain)}/settings/general"}>
Click here
</.styled_link>
<br />
<% end %>
</div>
}>
<ol class="list-disc space-y-1 ml-4 mt-1 mb-4">
<%= if ee?() and @finished? and not @success? and @attempts >= 3 do %>
<li>
<b>Need further help with your integration?</b>
<.styled_link href="https://plausible.io/contact">
Contact us
</.styled_link>
</li>
<% end %>
<%= if not @success? and not @modal? do %>
<li>
Need to see the snippet again?
<.styled_link href={"/#{URI.encode_www_form(@domain)}/snippet?flow=#{@flow}"}>
Click here
</.styled_link>
</li>
<li>
Run verification later and go to Site Settings?
<.styled_link href={"/#{URI.encode_www_form(@domain)}/settings/general"}>
Click here
</.styled_link>
</li>
<% end %>
</ol>
</:footer>
</PlausibleWeb.Components.Generic.focus_box>
</div>
"""
end

View File

@ -50,7 +50,7 @@ defmodule PlausibleWeb.Live.RegisterForm do
<div class="w-full max-w-md mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mb-4 mt-8">
<h2 class="text-xl font-black dark:text-gray-100">Invitation expired</h2>
<p class="mt-4 text-sm">
<p class="mt-4">
Your invitation has expired or been revoked. Please request fresh one or you can <%= link(
"sign up",
class: "text-indigo-600 hover:text-indigo-900",
@ -87,7 +87,11 @@ defmodule PlausibleWeb.Live.RegisterForm do
current_step="Register"
/>
<div class="w-full max-w-3xl mx-auto flex flex-shrink-0">
<PlausibleWeb.Components.Generic.focus_box>
<:title>
Enter your details
</:title>
<.form
:let={f}
for={@form}
@ -97,12 +101,9 @@ defmodule PlausibleWeb.Live.RegisterForm do
phx-change="validate"
phx-submit="register"
phx-trigger-action={@trigger_submit}
class="w-full max-w-md mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mb-4 mt-8"
>
<input name="user[register_action]" type="hidden" value={@live_action} />
<h2 class="text-xl font-black dark:text-gray-100">Enter your details</h2>
<%= if @invitation do %>
<.email_input field={f[:email]} for_invitation={true} />
<.name_input field={f[:name]} />
@ -113,10 +114,7 @@ defmodule PlausibleWeb.Live.RegisterForm do
<div class="my-4">
<div class="flex justify-between">
<label
for={f[:password].name}
class="block text-sm font-medium text-gray-700 dark:text-gray-300"
>
<label for={f[:password].name} class="block font-medium text-gray-700 dark:text-gray-300">
Password
</label>
<.password_length_hint minimum={12} field={f[:password]} />
@ -126,7 +124,7 @@ defmodule PlausibleWeb.Live.RegisterForm do
field={f[:password]}
strength={@password_strength}
phx-debounce={200}
class="dark:bg-gray-900 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-300"
class="dark:bg-gray-900 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-300"
/>
</div>
</div>
@ -134,9 +132,9 @@ defmodule PlausibleWeb.Live.RegisterForm do
<div class="my-4">
<label
for={f[:password_confirmation].name}
class="block text-sm font-medium text-gray-700 dark:text-gray-300"
class="block font-medium text-gray-700 dark:text-gray-300"
>
Password confirmation
Confirm password
</label>
<div class="mt-1">
<.input
@ -144,7 +142,7 @@ defmodule PlausibleWeb.Live.RegisterForm do
autocomplete="new-password"
field={f[:password_confirmation]}
phx-debounce={200}
class="dark:bg-gray-900 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-300"
class="dark:bg-gray-900 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-300"
/>
</div>
</div>
@ -176,9 +174,9 @@ defmodule PlausibleWeb.Live.RegisterForm do
<% submit_text =
if ce?() or @invitation do
"Create my account"
"Create my account"
else
"Start my free trial"
"Start my free trial"
end %>
<PlausibleWeb.Components.Generic.button
id="register"
@ -189,21 +187,21 @@ defmodule PlausibleWeb.Live.RegisterForm do
<%= submit_text %>
</PlausibleWeb.Components.Generic.button>
<p class="text-center text-gray-600 dark:text-gray-500 text-xs mt-4">
<p class="text-center text-gray-600 dark:text-gray-500 mt-4">
Already have an account? <%= link("Log in",
to: "/login",
class: "underline text-gray-800 dark:text-gray-50"
) %> instead.
) %>
</p>
</.form>
</div>
</PlausibleWeb.Components.Generic.focus_box>
"""
end
defp name_input(assigns) do
~H"""
<div class="my-4">
<label for={@field.name} class="block text-sm font-medium text-gray-700 dark:text-gray-300">
<label for={@field.name} class="block font-medium text-gray-700 dark:text-gray-300">
Full name
</label>
<div class="mt-1">
@ -211,7 +209,7 @@ defmodule PlausibleWeb.Live.RegisterForm do
field={@field}
placeholder="Jane Doe"
phx-debounce={200}
class="dark:bg-gray-900 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-300"
class="dark:bg-gray-900 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-300"
/>
</div>
</div>
@ -226,7 +224,6 @@ defmodule PlausibleWeb.Live.RegisterForm do
focus:border-indigo-500
block
w-full
sm:text-sm
border-gray-300
dark:border-gray-500
rounded-md
@ -248,7 +245,7 @@ defmodule PlausibleWeb.Live.RegisterForm do
~H"""
<div class="my-4">
<div class="flex justify-between">
<label for={@field.name} class="block text-sm font-medium text-gray-700 dark:text-gray-300">
<label for={@field.name} class="block font-medium text-gray-700 dark:text-gray-300">
Email
</label>
<p class="text-xs text-gray-500 mt-1">No spam, guaranteed.</p>

View File

@ -3,108 +3,111 @@
current_step="Activate account"
/>
<div class="w-full max-w-3xl mt-4 mx-auto flex">
<%= if @has_email_code? do %>
<%= form_for @conn, @form_submit_url, [class: "w-full max-w-lg mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-black dark:text-gray-100">
<%= if @has_any_memberships? do %>
Verify your email address
<% else %>
Activate your account
<% end %>
</h2>
<div class="mt-2 text-sm text-gray-500 dark:text-gray-200 leading-tight">
Please enter the 4-digit code we sent to <b><%= @conn.assigns[:current_user].email %></b>
</div>
<div class="mt-12 flex items-stretch flex-grow">
<div>
<%= text_input(f, :code,
class:
"tracking-widest font-medium shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-36 px-8 border-gray-300 dark:border-gray-500 rounded-l-md dark:text-gray-200 dark:bg-gray-900",
oninput:
"this.value=this.value.replace(/[^0-9]/g, ''); if (this.value.length >= 4) document.getElementById('submit').focus()",
onclick: "this.select();",
maxlength: "4",
placeholder: "••••",
style: "letter-spacing: 10px;",
required: "required"
) %>
</div>
<PlausibleWeb.Components.Generic.button id="submit" type="submit" class="rounded-l-none">
Activate &rarr;
</PlausibleWeb.Components.Generic.button>
</div>
<%= error_tag(assigns, :error) %>
<div class="mt-16 text-sm dark:text-gray-100">
Didn't receive an email?
</div>
<ol class="list-disc text-xs text-gray-500 leading-tight space-y-1 mt-1">
<li>Check your spam folder</li>
<li>
<%= link("Send a new code",
class: "underline text-indigo-600",
to: "/activate/request-code",
method: :post
) %> to <%= @conn.assigns[:current_user].email %>
</li>
<%= if ee?() do %>
<li>
<a class="underline text-indigo-600" href="https://plausible.io/contact">
Contact us
</a>
if the problem persists
</li>
<% else %>
<li>
Ask on our <%= link("community-supported forum",
to: "https://github.com/plausible/analytics/discussions",
class: "text-indigo-600 underline"
) %>
</li>
<% end %>
</ol>
<div class="mt-4 text-sm dark:text-gray-100">
Entered the wrong email address?
</div>
<ul class="list-disc text-xs text-gray-500 leading-tight mt-1">
<%= if @has_any_memberships? do %>
<li>
<%= link("Change email back to",
class: "underline text-indigo-600",
to: "/settings/email/cancel",
method: "post"
) %> to <%= @conn.assigns[:current_user].previous_email %>
</li>
<% else %>
<li>
<%= link("Delete this account",
class: "underline text-indigo-600",
to: "/me?redirect=/register",
method: "delete",
data: [confirm: "Deleting your account cannot be reversed. Are you sure?"]
) %> and start over
</li>
<% end %>
</ul>
<PlausibleWeb.Components.Generic.focus_box>
<:title>
<%= if @has_email_code? do %>
<%= if @has_any_memberships? do %>
Verify your email address
<% else %>
Activate your account
<% end %>
<% else %>
Activate your account
<% end %>
<% else %>
<div class="w-full max-w-lg mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mb-4 mt-8">
<h2 class="text-xl font-black dark:text-gray-100">Activate your account</h2>
</:title>
<div class="mt-2 text-sm text-gray-500 dark:text-gray-200 leading-tight">
A 4-digit activation code will be sent to <b><%= @conn.assigns[:current_user].email %></b>
</div>
<:subtitle :if={@has_email_code?}>
<p class="truncate">
Please enter the 4-digit code we sent to <b><%= @conn.assigns[:current_user].email %></b>
</p>
</:subtitle>
<%= error_tag(assigns, :error) %>
<:subtitle :if={!@has_email_code?}>
<p class="truncate">
A 4-digit activation code will be sent to <b><%= @conn.assigns[:current_user].email %></b>
</p>
</:subtitle>
<%= button("Request activation code",
to: "/activate/request-code",
method: :post,
class: "button mt-12"
<div :if={@has_email_code?}>
<%= form_for @conn, @form_submit_url, [], fn f -> %>
<%= text_input(f, :code,
class:
"tracking-widest font-medium shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-36 px-8 border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-200 dark:bg-gray-900 text-center",
oninput:
"this.value=this.value.replace(/[^0-9]/g, ''); if (this.value.length >= 4) document.getElementById('submit').focus()",
onclick: "this.select();",
maxlength: "4",
placeholder: "••••",
style: "letter-spacing: 10px;",
required: "required"
) %>
</div>
<% end %>
</div>
<PlausibleWeb.Components.Generic.button id="submit" type="submit" class="w-full mt-8">
Activate
</PlausibleWeb.Components.Generic.button>
<% end %>
</div>
<%= error_tag(assigns, :error) %>
<div :if={!@has_email_code?}>
<%= button("Request activation code",
to: "/activate/request-code",
method: :post,
class: "w-full button"
) %>
</div>
<:footer :if={@has_email_code?}>
<b>Didn't receive an email?</b>
<ol class="list-disc space-y-1 ml-4 mt-1 mb-4">
<li>Check your spam folder</li>
<li>
<%= link("Send a new code",
class: "underline text-indigo-600",
to: "/activate/request-code",
method: :post
) %> to <%= @conn.assigns[:current_user].email %>
</li>
<%= if ee?() do %>
<li>
<a class="underline text-indigo-600" href="https://plausible.io/contact">
Contact us
</a>
if the problem persists
</li>
<% else %>
<li>
Ask on our <%= link("community-supported forum",
to: "https://github.com/plausible/analytics/discussions",
class: "text-indigo-600 underline"
) %>
</li>
<% end %>
</ol>
<b>Entered the wrong email address?</b>
<ol class="list-disc space-y-1 ml-4 mt-1">
<%= if @has_any_memberships? do %>
<li>
<%= link("Change email back to",
class: "underline text-indigo-600",
to: "/settings/email/cancel",
method: "post"
) %> to <%= @conn.assigns[:current_user].previous_email %>
</li>
<% else %>
<li>
<%= link("Delete this account",
class: "underline text-indigo-600",
to: "/me?redirect=/register",
method: "delete",
data: [confirm: "Deleting your account cannot be reversed. Are you sure?"]
) %> and start over
</li>
<% end %>
</ol>
</:footer>
</PlausibleWeb.Components.Generic.focus_box>

View File

@ -1,49 +1,48 @@
<div class="w-full max-w-3xl mt-4 mx-auto flex">
<div class="w-full max-w-lg mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mb-4 mt-8">
<h2 class="text-xl font-black dark:text-gray-100">
<%= if @from_setup do %>
Setup Two-Factor Authentication
<% else %>
Your New Recovery Codes
<% end %>
</h2>
<PlausibleWeb.Components.Generic.focus_box>
<:title>
<%= if @from_setup do %>
Setup Two-Factor Authentication
<% else %>
Your New Recovery Codes
<% end %>
</:title>
<div class="text-sm mt-2 text-gray-500 dark:text-gray-200 leading-tight">
Use these recovery codes to log in if you lose access to the authenticator application. Store them somewhere safe!
<div
id="recovery-codes-list"
class="font-mono border-2 border-dotted border-gray-200 dark:border-gray-700 rounded-md text-gray-600 dark:text-gray-200 text-lg bg-gray-100 dark:bg-gray-900 p-2 mt-6 flex flex-wrap"
>
<%= for code <- @recovery_codes do %>
<div class="basis-1/2 text-center"><%= code %></div>
<% end %>
</div>
</div>
<:subtitle>
Use these recovery codes to log in if you lose access to the authenticator application. Store them somewhere safe!
</:subtitle>
<div class="mt-6 flex sm:flex-row flex-col justify-between">
<button onclick="print(); event.stopPropagation();" id="print" class="button">
Print Codes <Heroicons.printer class="h-4 w-4 ml-2 mt-1" />
</button>
<button
onclick="var list = document.getElementById('recovery-codes-list'); var selection = getSelection(); selection.removeAllRanges(); var range = createRange(); range.selectNodeContents(list); selection.addRange(range); document.execCommand('copy'); selection.removeAllRanges(); event.stopPropagation(); document.getElementById('copy-base-icon').classList.add('hidden'); document.getElementById('copy-done-icon').classList.remove('hidden'); setTimeout(function() { document.getElementById('copy-done-icon').classList.add('hidden'); document.getElementById('copy-base-icon').classList.remove('hidden'); }, 2000)"
id="copy"
class="button sm:mt-0 mt-3"
>
Copy to Clipboard
<span id="copy-base-icon">
<Heroicons.document_duplicate class="h-4 w-4 ml-2 mt-1" />
</span>
<span id="copy-done-icon" class="hidden">
<Heroicons.check class="h-4 w-4 ml-2 mt-1" />
</span>
</button>
<button
id="finish"
class="button sm:mt-0 mt-3"
onclick={"location.replace('#{Routes.auth_path(@conn, :user_settings) <> "#setup-2fa"}')"}
>
Finish
</button>
</div>
<div
id="recovery-codes-list"
class="font-mono border-2 border-dotted border-gray-200 dark:border-gray-700 rounded-md text-gray-600 dark:text-gray-200 text-lg bg-gray-100 dark:bg-gray-900 p-2 mt-6 flex flex-wrap"
>
<%= for code <- @recovery_codes do %>
<div class="basis-1/2 text-center"><%= code %></div>
<% end %>
</div>
</div>
<div class="mt-6">
<button onclick="print(); event.stopPropagation();" id="print" class="button w-full">
Print Codes <Heroicons.printer class="h-4 w-4 ml-2 mt-1" />
</button>
<button
onclick="var list = document.getElementById('recovery-codes-list'); var selection = getSelection(); selection.removeAllRanges(); var range = createRange(); range.selectNodeContents(list); selection.addRange(range); document.execCommand('copy'); selection.removeAllRanges(); event.stopPropagation(); document.getElementById('copy-base-icon').classList.add('hidden'); document.getElementById('copy-done-icon').classList.remove('hidden'); setTimeout(function() { document.getElementById('copy-done-icon').classList.add('hidden'); document.getElementById('copy-base-icon').classList.remove('hidden'); }, 2000)"
id="copy"
class="button w-full mt-4"
>
Copy to Clipboard
<span id="copy-base-icon">
<Heroicons.document_duplicate class="h-4 w-4 ml-2 mt-1" />
</span>
<span id="copy-done-icon" class="hidden">
<Heroicons.check class="h-4 w-4 ml-2 mt-1" />
</span>
</button>
<button
id="finish"
class="button w-full mt-4"
onclick={"location.replace('#{Routes.auth_path(@conn, :user_settings) <> "#setup-2fa"}')"}
>
Finish
</button>
</div>
</PlausibleWeb.Components.Generic.focus_box>

View File

@ -1,71 +1,73 @@
<div class="w-full max-w-2xl mt-4 mx-auto flex">
<div class="w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mb-4 mt-8">
<h2 class="text-xl font-black dark:text-gray-100">
Setup Two-Factor Authentication
</h2>
<PlausibleWeb.Components.Generic.focus_box>
<:title>
Setup Two-Factor Authentication
</:title>
<div class="text-sm mt-2 text-gray-500 dark:text-gray-200">
Link your Plausible account to the authenticator app you have installed either on your phone or computer.
<div class="flex flex-col sm:flex-row items-center sm:items-start">
<div class="mt-8">
<div class="border-2 border-gray-300 inline-block p-2 dark:bg-white">
<PlausibleWeb.Components.TwoFactor.qr_code text={@totp_uri} />
</div>
</div>
<:subtitle>
Link your Plausible account to the authenticator app you have installed either on your phone or computer.
</:subtitle>
<div class="mt-8 sm:ml-4">
<ol>
<li class="flex items-start">
<div class="flex-shrink-0 h-5 w-5 relative flex items-center justify-center">
<div class="h-2 w-2 bg-gray-300 dark:bg-gray-700 rounded-full"></div>
</div>
Open the authenticator application
</li>
<li class="mt-1 flex items-start">
<div class="flex-shrink-0 h-5 w-5 relative flex items-center justify-center">
<div class="h-2 w-2 bg-gray-300 dark:bg-gray-700 rounded-full"></div>
</div>
Tap Scan a QR Code
</li>
<li class="mt-1 flex items-start">
<div class="flex-shrink-0 h-5 w-5 relative flex items-center justify-center">
<div class="h-2 w-2 bg-gray-300 dark:bg-gray-700 rounded-full"></div>
</div>
Scan this code with your phone camera or paste the code manually
</li>
</ol>
<:footer>
<p>
Changed your mind?
<a
href={Routes.auth_path(@conn, :user_settings) <> "#setup-2fa"}
class="underline text-indigo-600"
>
Go back to Settings
</a>
</p>
</:footer>
<div class="sm:ml-2">
<PlausibleWeb.Live.Components.Form.input_with_clipboard
id="secret"
name="secret_clipboard"
label="Code"
value={@secret}
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"
/>
</div>
</div>
<div class="flex flex-col sm:flex-row items-center sm:items-start">
<div class="mt-8">
<div class="border-2 border-gray-300 inline-block p-2 dark:bg-white">
<PlausibleWeb.Components.TwoFactor.qr_code text={@totp_uri} />
</div>
</div>
<div class="mt-6 flex flex-col-reverse sm:flex-row justify-between items-center">
<p class="text-sm mt-4 sm:mt-0">
Changed your mind?
<a
href={Routes.auth_path(@conn, :user_settings) <> "#setup-2fa"}
class="underline text-indigo-600"
>
Go back to Settings
</a>
</p>
<.unstyled_link
id="proceed"
class="button sm:w-auto w-full"
href={Routes.auth_path(@conn, :verify_2fa_setup_form)}
>
Proceed &rarr;
</.unstyled_link>
<div class="mt-8 sm:ml-4">
<ol>
<li class="flex items-start">
<div class="flex-shrink-0 h-5 w-5 relative flex items-center justify-center">
<div class="h-2 w-2 bg-gray-300 dark:bg-gray-700 rounded-full"></div>
</div>
Open the authenticator application
</li>
<li class="mt-1 flex items-start">
<div class="flex-shrink-0 h-5 w-5 relative flex items-center justify-center">
<div class="h-2 w-2 bg-gray-300 dark:bg-gray-700 rounded-full"></div>
</div>
Tap Scan a QR Code
</li>
<li class="mt-1 flex items-start">
<div class="flex-shrink-0 h-5 w-5 relative flex items-center justify-center">
<div class="h-2 w-2 bg-gray-300 dark:bg-gray-700 rounded-full"></div>
</div>
Scan this code with your phone camera or paste the code manually
</li>
</ol>
<div class="sm:ml-2">
<PlausibleWeb.Live.Components.Form.input_with_clipboard
id="secret"
name="secret_clipboard"
label="Code"
value={@secret}
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"
/>
</div>
</div>
</div>
</div>
<div class="mt-6 flex flex-col-reverse sm:flex-row justify-between items-center">
<.unstyled_link
id="proceed"
class="button w-full"
href={Routes.auth_path(@conn, :verify_2fa_setup_form)}
>
Proceed
</.unstyled_link>
</div>
</PlausibleWeb.Components.Generic.focus_box>

View File

@ -1,24 +0,0 @@
<%= form_for @conn, "/login", [class: "w-full max-w-md mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mt-8"], fn f -> %>
<h1 class="text-xl font-black dark:text-gray-100"><%= Phoenix.Flash.get(@flash, :login_title) || "Enter your email and password" %></h1>
<%= if Phoenix.Flash.get(@flash, :login_instructions) do %>
<p class="text-gray-500 text-sm mt-1 mb-2"><%= Phoenix.Flash.get(@flash, :login_instructions) %></p>
<% end %>
<%= if @conn.assigns[:error] do %>
<div class="text-red-500 text-xs italic mt-4"><%= @conn.assigns[:error] %></div>
<% end %>
<div class="my-4 mt-8">
<%= label f, :email, class: "block text-gray-700 dark:text-gray-300 text-sm font-bold mb-2" %>
<%= email_input f, :email, autocomplete: "username", class: "bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal appearance-none focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500", placeholder: "user@example.com" %>
</div>
<div class="my-4">
<%= label f, :password, class: "block text-gray-700 dark:text-gray-300 text-sm font-bold mb-2" %>
<%= password_input f, :password, id: "current-password", autocomplete: "current-password", class: "transition bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal appearance-none focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500" %>
<p class="text-gray-500 text-xs my-2">Forgot password? <a href="/password/request-reset" class="underline text-gray-800 dark:text-gray-50">Click here</a> to reset it.</p>
</div>
<%= submit "Login →", class: "button mt-4 w-full" %>
<%= if Keyword.fetch!(Application.get_env(:plausible, :selfhost),:disable_registration) == false do %>
<p class="text-center text-gray-500 text-xs mt-4">
Don't have an account? <%= link("Register", to: "/register", class: "text-gray-800 dark:text-gray-50 underline") %> instead.
</p>
<% end %>
<% end %>

View File

@ -0,0 +1,58 @@
<PlausibleWeb.Components.Generic.focus_box>
<:title>
<%= Phoenix.Flash.get(@flash, :login_title) || "Enter your account credentials" %>
</:title>
<:subtitle>
<%= if Phoenix.Flash.get(@flash, :login_instructions) do %>
<p class="text-gray-500 mt-1 mb-2">
<%= Phoenix.Flash.get(@flash, :login_instructions) %>
</p>
<% end %>
</:subtitle>
<%= form_for @conn, "/login", [], fn f -> %>
<div class="my-4 mt-8">
<%= label(f, :email, class: "block text-gray-700 dark:text-gray-300 mb-2") %>
<%= email_input(f, :email,
autocomplete: "username",
class:
"bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal appearance-none focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500",
placeholder: "user@example.com"
) %>
</div>
<div class="my-4">
<%= label(f, :password, class: "block text-gray-700 dark:text-gray-300 mb-2") %>
<%= password_input(f, :password,
id: "current-password",
autocomplete: "current-password",
class:
"transition bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal appearance-none focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500"
) %>
</div>
<%= if @conn.assigns[:error] do %>
<div class="text-red-500 mt-4"><%= @conn.assigns[:error] %></div>
<% end %>
<%= submit("Log in", class: "button mt-4 w-full") %>
<% end %>
<:footer>
<ol class="list-disc space-y-1 ml-4 mt-1 mb-4">
<%= if Keyword.fetch!(Application.get_env(:plausible, :selfhost),:disable_registration) == false do %>
<li>
Don't have an account? <%= link("Register",
to: "/register",
class: "text-gray-800 dark:text-gray-50 underline"
) %> instead.
</li>
<% end %>
<li>
Forgot password?
<a href="/password/request-reset" class="underline text-gray-800 dark:text-gray-50">
Click here
</a>
to reset it.
</li>
</ol>
</:footer>
</PlausibleWeb.Components.Generic.focus_box>

View File

@ -1,22 +0,0 @@
<%= form_for @conn, "/password/request-reset", [class: "max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mt-8"], fn f -> %>
<h1 class="text-xl font-black dark:text-gray-100">Reset your password</h1>
<div class="mt-4 dark:text-gray-100">Enter your email so we can send a password reset link</div>
<div class="my-4 mt-8">
<%= email_input f, :email, class: "transition bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500", placeholder: "user@example.com" %>
</div>
<%= if @conn.assigns[:error] do %>
<div class="text-red-500 text-xs italic my-2"><%= @conn.assigns[:error] %></div>
<% end %>
<%= if PlausibleWeb.Captcha.enabled?() do %>
<div class="mt-4">
<div class="h-captcha" data-sitekey="<%= PlausibleWeb.Captcha.sitekey() %>"></div>
<%= if assigns[:captcha_error] do %>
<div class="text-red-500 text-xs italic mt-3"><%= @captcha_error %></div>
<% end %>
<script src="https://hcaptcha.com/1/api.js" async defer></script>
</div>
<% end %>
<%= submit "Send reset link →", class: "button mt-4 w-full" %>
<% end %>

View File

@ -0,0 +1,35 @@
<PlausibleWeb.Components.Generic.focus_box>
<:title>
Reset your password
</:title>
<:subtitle>
Enter your email so we can send a password reset link
</:subtitle>
<%= form_for @conn, "/password/request-reset", [], fn f -> %>
<div class="my-4 mt-8">
<%= email_input(f, :email,
class:
"transition bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500",
placeholder: "user@example.com"
) %>
</div>
<%= if @conn.assigns[:error] do %>
<div class="text-red-500 my-2"><%= @conn.assigns[:error] %></div>
<% end %>
<%= if PlausibleWeb.Captcha.enabled?() do %>
<div class="mt-4">
<div class="h-captcha" data-sitekey={PlausibleWeb.Captcha.sitekey()}></div>
<%= if assigns[:captcha_error] do %>
<div class="text-red-500 text-xs mt-3"><%= @captcha_error %></div>
<% end %>
<script src="https://hcaptcha.com/1/api.js" async defer>
</script>
</div>
<% end %>
<%= submit("Send reset link", class: "button mt-4 w-full") %>
<% end %>
</PlausibleWeb.Components.Generic.focus_box>

View File

@ -1,14 +1,33 @@
<div class="w-full max-w-3xl mt-4 mx-auto flex">
<PlausibleWeb.Components.Generic.focus_box>
<:title>
Enter Your 2FA Code
</:title>
<:subtitle>
Enter the code from your authenticator application before it expires or wait for a new one.
</:subtitle>
<:footer>
<ol class="list-disc space-y-1 ml-4 mt-1 mb-4">
<li>
Can't access your authenticator application?
<.styled_link href={Routes.auth_path(@conn, :verify_2fa_recovery_code_form)}>
Use recovery code
</.styled_link>
</li>
<li :if={ee?()}>
Lost your recovery codes?
<.styled_link href="https://plausible.io/contact">
Contact us
</.styled_link>
</li>
</ol>
</:footer>
<%= form_for @conn.params, Routes.auth_path(@conn, :verify_2fa), [
class: "w-full max-w-lg mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mb-4 mt-8",
onsubmit: "document.getElementById('verify-button').disabled = true"
], fn f -> %>
<h2 class="text-xl font-black dark:text-gray-100">
Enter Your 2FA Code
</h2>
<div class="text-sm mt-2 text-gray-500 dark:text-gray-200 leading-tight">
Enter the code from your authenticator application before it expires or wait for a new one.
<div class="mt-2 text-gray-500 dark:text-gray-200 leading-tight">
<PlausibleWeb.Components.TwoFactor.verify_2fa_input form={f} field={:code} class="mt-6" />
<div class="mt-4 flex flex-inline items-center sm:justify-start justify-center">
@ -20,24 +39,6 @@
Trust this device for <%= @remember_2fa_days %> days
</label>
</div>
<div class="mt-6 flex flex-row justify-between items-center">
<p class="text-sm">
Can't access your authenticator application?
<a
href={Routes.auth_path(@conn, :verify_2fa_recovery_code_form)}
class="underline text-indigo-600"
>
Use recovery code
</a>
<%= if ee?() do %>
<br /> Lost your recovery codes?
<a href="https://plausible.io/contact" class="underline text-indigo-600">
Contact us
</a>
<% end %>
</p>
</div>
</div>
<% end %>
</div>
</PlausibleWeb.Components.Generic.focus_box>

View File

@ -1,38 +1,37 @@
<div class="w-full max-w-3xl mt-4 mx-auto flex">
<div class="w-full max-w-lg mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mb-4 mt-8">
<h2 class="text-xl font-black dark:text-gray-100">
Setup Two-Factor Authentication
</h2>
<PlausibleWeb.Components.Generic.focus_box>
<:title>
Setup Two-Factor Authentication
</:title>
<div class="text-sm mt-2 text-gray-500 dark:text-gray-200 leading-tight">
<%= form_for @conn.params, Routes.auth_path(@conn, :verify_2fa_setup), [
id: "verify-2fa-form",
onsubmit: "document.getElementById('verify-button').disabled = true"
], fn f -> %>
Enter the code from your authenticator application before it expires or wait for a new one.
<PlausibleWeb.Components.TwoFactor.verify_2fa_input form={f} field={:code} class="mt-6" />
<:subtitle>
Enter the code from your authenticator application before it expires or wait for a new one.
</:subtitle>
<:footer>
<p>
Changed your mind?
<a
href={Routes.auth_path(@conn, :user_settings) <> "#setup-2fa"}
class="underline text-indigo-600"
>
Go back to Settings
</a>
</p>
<p>
<%= form_for @conn.params, Routes.auth_path(@conn, :initiate_2fa_setup), [id: "start-over-form"], fn _f -> %>
Having trouble?
<button class="underline text-indigo-600">
Start over
</button>
<% end %>
</p>
</:footer>
<div class="mt-6 flex flex-col">
<p class="text-sm">
Changed your mind?
<a
href={Routes.auth_path(@conn, :user_settings) <> "#setup-2fa"}
class="underline text-indigo-600"
>
Go back to Settings
</a>
</p>
<p class="text-sm">
<%= form_for @conn.params, Routes.auth_path(@conn, :initiate_2fa_setup), [id: "start-over-form"], fn _f -> %>
Having trouble?
<button class="underline text-indigo-600">
Start over
</button>
<% end %>
</p>
</div>
</div>
</div>
</div>
<%= form_for @conn.params, Routes.auth_path(@conn, :verify_2fa_setup), [
id: "verify-2fa-form",
onsubmit: "document.getElementById('verify-button').disabled = true"
], fn f -> %>
<PlausibleWeb.Components.TwoFactor.verify_2fa_input form={f} field={:code} class="mt-6" />
<% end %>
</PlausibleWeb.Components.Generic.focus_box>

View File

@ -1,34 +0,0 @@
<div class="mt-12 w-full md:max-w-xl md:mx-auto bg-white dark:bg-gray-800 md:shadow-md md:rounded px-8 py-6">
<p class="text-gray-900 text-xl font-black dark:text-gray-100">
Welcome to Plausible!
</p>
<p class="mt-4 text-gray-600 dark:text-gray-200">
<a href="https://plausible.io/" class="border-b text-indigo-700 font-semibold dark:text-indigo-400 border-indigo-700 dark:border-indigo-500">Plausible Analytics</a> is a simple, open source, lightweight (&lt; 1 KB) and privacy-friendly alternative to Google Analytics. We're completely independent and solely funded by our 10,000+ paying subscribers. Read more <a href="https://plausible.io/about" class="border-b font-semibold text-indigo-700 dark:text-indigo-400 border-indigo-700 dark:border-indigo-500">about us.</a>
</p>
<ul class="mt-6 flex flex-wrap text-gray-700 dark:text-gray-300 space-y-1 md:space-y-0">
<li class="w-full md:w-1/2 md:order-1">
<%= link to: Routes.auth_path(@conn, :login), class: "flex items-center" do %>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-log-in h-4 w-4"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"></path><polyline points="10 17 15 12 10 7"></polyline><line x1="15" y1="12" x2="3" y2="12"></line></svg>
<span class="ml-2 font-semibold underline text-indigo-700 dark:text-indigo-400">Login</span>
<% end %>
</li>
<li class="w-full md:mt-1 md:w-1/2 md:order-3">
<%= link to: Routes.auth_path(@conn, :register_form), class: "flex items-center" do %>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-user-plus h-4 w-4"><path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="8.5" cy="7" r="4"></circle><line x1="20" y1="8" x2="20" y2="14"></line><line x1="23" y1="11" x2="17" y2="11"></line></svg>
<span class="ml-2 font-semibold underline text-indigo-700 dark:text-indigo-400">Register</span>
<% end %>
</li>
<li class="w-full md:w-1/2 md:order-2">
<a href="https://plausible.io/docs" class="flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-book-open h-4 w-4"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path></svg>
<span class="ml-2 font-semibold underline text-indigo-700 dark:text-indigo-400">Guides &amp; Docs</span>
</a>
</li>
<li class="w-full md:mt-1 md:w-1/2 md:order-4">
<a href="https://twitter.com/plausiblehq" class="flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-twitter h-4 w-4"><path d="M23 3a10.9 10.9 0 0 1-3.14 1.53 4.48 4.48 0 0 0-7.86 3v1A10.66 10.66 0 0 1 3 4s-4 9 5 13a11.64 11.64 0 0 1-7 2c9 5 20 0 20-11.5a4.5 4.5 0 0 0-.08-.83A7.72 7.72 0 0 0 23 3z"></path></svg>
<span class="ml-2 font-semibold underline text-indigo-700 dark:text-indigo-400">Follow on Twitter</span>
</a>
</li>
</ul>
</div>

View File

@ -0,0 +1,40 @@
<PlausibleWeb.Components.Generic.focus_box>
<:title>
Welcome to Plausible!
</:title>
<p>
<.styled_link href="https://plausible.io/">
Plausible Analytics
</.styled_link>
is a simple, open source, lightweight (&lt;&nbsp;1&nbsp;KB) and privacy-friendly alternative to Google Analytics. We're completely independent and solely funded by our 10,000+ paying subscribers. Read more
<.styled_link href="https://plausible.io/about">
about us.
</.styled_link>
</p>
<:footer>
<ol class="list-disc space-y-1 ml-4 mt-1 mb-4">
<li>
<.styled_link href={Routes.auth_path(@conn, :login)}>
Login
</.styled_link>
</li>
<li>
<.styled_link href={Routes.auth_path(@conn, :register_form)}>
Register
</.styled_link>
</li>
<li>
<.styled_link href="https://plausible.io/docs">
Guides & Docs
</.styled_link>
</li>
<li>
<.styled_link href="https://twitter.com/plausiblehq">
Follow on Twitter
</.styled_link>
</li>
</ol>
</:footer>
</PlausibleWeb.Components.Generic.focus_box>

View File

@ -1,9 +1,11 @@
<PlausibleWeb.Components.FlowProgress.render flow={@flow} current_step="Add site info" />
<div class="w-full max-w-3xl mt-4 mx-auto flex">
<%= form_for @changeset, @form_submit_url, [class: "max-w-lg w-full mx-auto bg-white dark:bg-gray-800 shadow-lg rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-black dark:text-gray-100 mb-4">Your website details</h2>
<PlausibleWeb.Components.Generic.focus_box>
<:title>
Add website info
</:title>
<%= form_for @changeset, @form_submit_url, [class: ""], fn f -> %>
<PlausibleWeb.Components.Billing.Notice.limit_exceeded
:if={@site_limit_exceeded?}
current_user={@current_user}
@ -42,17 +44,17 @@
<% end %>
<div class="my-6">
<%= label(f, :domain, class: "block text-sm font-medium text-gray-700 dark:text-gray-300") %>
<p class="text-gray-500 dark:text-gray-400 text-xs mt-1">
<%= label(f, :domain, class: "block font-medium dark:text-gray-300") %>
<p class="text-gray-500 dark:text-gray-400 mt-1 text-sm">
Just the naked domain or subdomain without 'www'
</p>
<div class="mt-2 flex rounded-md shadow-sm">
<span class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 dark:border-gray-500 bg-gray-50 dark:bg-gray-850 text-gray-500 dark:text-gray-400 sm:text-sm">
<span class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 dark:border-gray-500 bg-gray-50 dark:bg-gray-850 dark:text-gray-400">
https://
</span>
<%= text_input(f, :domain,
class:
"focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-800 flex-1 block w-full px-3 py-2 rounded-none rounded-r-md sm:text-sm border-gray-300 dark:border-gray-500 dark:bg-gray-900 dark:text-gray-300",
"focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-800 flex-1 block w-full px-3 py-2 rounded-none rounded-r-md border-gray-300 dark:border-gray-500 dark:bg-gray-900 dark:text-gray-300",
placeholder: "example.com",
disabled: @site_limit_exceeded?
) %>
@ -61,9 +63,9 @@
</div>
<div class="my-6">
<%= label(f, :timezone, "Reporting Timezone",
class: "block text-sm font-medium text-gray-700 dark:text-gray-300"
class: "block font-medium text-gray-700 dark:text-gray-300"
) %>
<p class="text-gray-500 dark:text-gray-400 text-xs mt-1">
<p class="text-gray-500 dark:text-gray-400 text-sm mt-1">
To make sure we agree on what 'today' means
</p>
@ -78,24 +80,24 @@
</div>
</div>
<script>
var option;
var option;
if (typeof Intl !== "undefined") {
var timezoneName = Intl.DateTimeFormat().resolvedOptions().timeZone
option = document.querySelector('#tz-select option[value="' + timezoneName + '"]')
}
if (typeof Intl !== "undefined") {
var timezoneName = Intl.DateTimeFormat().resolvedOptions().timeZone
option = document.querySelector('#tz-select option[value="' + timezoneName + '"]')
}
if (!option) {
var offset = (new Date()).getTimezoneOffset()
option = document.querySelector('#tz-select option[offset="' + offset + '"]')
option = document.querySelector('#tz-select option[offset="' + offset + '"]')
}
if (option) { option.selected = "selected"}
</script>
<%= submit("Add snippet",
<%= submit("Add snippet",
class: "button mt-4 w-full disabled:cursor-not-allowed",
disabled: @site_limit_exceeded?
) %>
<% end %>
</div>
</PlausibleWeb.Components.Generic.focus_box>

View File

@ -8,23 +8,44 @@
current_step="Install snippet"
/>
<div class="w-full max-w-3xl mt-4 mx-auto flex">
<%= form_for @conn, @form_submit_url, [class: "max-w-lg w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-bold dark:text-gray-100">Add JavaScript snippet</h2>
<div class="mt-4">
<p :if={Plausible.Verification.enabled?()} class="dark:text-gray-100">
Include this snippet in the <code>&lt;head&gt;</code>
section of your website.<br />To verify your integration, click the button below to confirm that everything is working correctly.
</p>
<p :if={not Plausible.Verification.enabled?()} class="dark:text-gray-100">
Paste this snippet in the <code>&lt;head&gt;</code> of your website.
</p>
<PlausibleWeb.Components.Generic.focus_box>
<:title>
Add JavaScript snippet
</:title>
<:subtitle>
<p :if={Plausible.Verification.enabled?()} class="dark:text-gray-100">
Include this snippet in the <code>&lt;head&gt;</code>
section of your website.<br />To verify your integration, click the button below to confirm that everything is working correctly.
</p>
<p :if={not Plausible.Verification.enabled?()} class="dark:text-gray-100">
Paste this snippet in the <code>&lt;head&gt;</code> of your website.
</p>
</:subtitle>
<:footer>
<ol class="list-disc space-y-1 ml-4 mt-1 mb-4">
<li>
On WordPress? We have
<.styled_link new_tab href="https://plausible.io/wordpress-analytics-plugin">
a plugin
</.styled_link>
</li>
<li>
See more
<.styled_link new_tab href="https://plausible.io/docs/integration-guides">
integration guides
</.styled_link>
</li>
</ol>
</:footer>
<%= form_for @conn, @form_submit_url, [], fn f -> %>
<div>
<div class="relative">
<%= textarea(f, :domain,
id: "snippet_code",
class:
"transition overflow-hidden bg-gray-100 dark:bg-gray-900 appearance-none border border-transparent rounded w-full p-2 pr-6 text-gray-700 dark:text-gray-300 leading-normal appearance-none focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-400 dark:focus:border-gray-500 text-xs mt-4 resize-none",
"transition overflow-hidden bg-gray-100 dark:bg-gray-900 appearance-none border border-transparent rounded w-full p-2 pr-6 text-gray-700 dark:text-gray-300 leading-normal appearance-none focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-400 dark:focus:border-gray-500 font-mono mt-4 resize-none text-xs",
value: render_snippet(@site),
rows: 3,
readonly: "readonly"
@ -32,7 +53,7 @@
<a
onclick="var textarea = document.getElementById('snippet_code'); textarea.focus(); textarea.select(); document.execCommand('copy');"
href="javascript:void(0)"
class="no-underline text-indigo-500 text-sm hover:underline"
class="no-underline text-indigo-500 hover:underline"
>
<svg
class="absolute text-indigo-500"
@ -54,30 +75,17 @@
</div>
</div>
<div class="mt-2 dark:text-gray-100">
<p class="text-sm">
On WordPress? We have
<.styled_link new_tab href="https://plausible.io/wordpress-analytics-plugin">
a plugin
</.styled_link>
</p>
<p class="text-sm">
See more
<.styled_link new_tab href="https://plausible.io/docs/integration-guides">
integration guides
</.styled_link>
</p>
</div>
<% button_label =
if Plausible.Verification.enabled?() do
"Verify your integration to start collecting data"
"Verify your integration to start collecting data"
else
"Start collecting data"
"Start collecting data"
end %>
<%= link(button_label,
class: "button mt-4 w-full",
to: @form_submit_url
) %>
<% end %>
</div>
</PlausibleWeb.Components.Generic.focus_box>
<div class="w-full max-w-3xl mt-4 mx-auto flex"></div>

View File

@ -17,64 +17,62 @@
current_step="Verify snippet"
/>
<div class="w-full max-w-md mx-auto mt-8">
<%= if @site.locked do %>
<div
class="w-full px-4 py-4 text-sm font-bold text-center text-yellow-800 bg-yellow-100 rounded transition"
style="top: 91px"
role="alert"
>
<p>This dashboard is actually locked. You are viewing it with super-admin access</p>
</div>
<% end %>
<%= if @site.locked do %>
<div
:if={not Plausible.Verification.enabled?()}
class="bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-16 relative text-center"
class="w-full px-4 py-4 text-sm font-bold text-center text-yellow-800 bg-yellow-100 rounded transition"
style="top: 91px"
role="alert"
>
<h2 class="text-xl font-bold dark:text-gray-100">Waiting for first pageview</h2>
<h2 class="text-xl font-bold dark:text-gray-100">on <%= @site.domain %></h2>
<div class="my-44">
<div class="block pulsating-circle top-1/2 left-1/2"></div>
<p class="text-gray-600 dark:text-gray-400 text-xs absolute left-0 bottom-0 mb-6 w-full text-center leading-normal">
Need to see the snippet again?
<.styled_link href={"/#{URI.encode_www_form(@site.domain)}/snippet?flow=#{@conn.params[~s|flow|]}"}>
Click here
</.styled_link>
<br /> Not working?
<.styled_link
new_tab
href="https://plausible.io/docs/troubleshoot-integration#check-for-the-plausible-snippet-in-your-source-code"
>
Troubleshoot the integration
</.styled_link>
<br />
<span :if={ee?()}>
Check the
<.styled_link href={Routes.site_path(@conn, :settings_general, @site.domain)}>
site settings
</.styled_link>
to invite team members, <br /> import historical stats and more.
</span>
<span :if={ce?()}>
Still not working? Ask on our
<.styled_link new_tab href="https://github.com/plausible/analytics/discussions">
community-supported forum
</.styled_link>
</span>
</p>
</div>
<p>This dashboard is actually locked. You are viewing it with super-admin access</p>
</div>
<% end %>
<div
:if={not Plausible.Verification.enabled?()}
class="bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-16 relative text-center"
>
<h2 class="text-xl font-bold dark:text-gray-100">Waiting for first pageview</h2>
<h2 class="text-xl font-bold dark:text-gray-100">on <%= @site.domain %></h2>
<div class="my-44">
<div class="block pulsating-circle top-1/2 left-1/2"></div>
<p class="text-gray-600 dark:text-gray-400 text-xs absolute left-0 bottom-0 mb-6 w-full text-center leading-normal">
Need to see the snippet again?
<.styled_link href={"/#{URI.encode_www_form(@site.domain)}/snippet?flow=#{@conn.params[~s|flow|]}"}>
Click here
</.styled_link>
<%= if Plausible.Verification.enabled?(),
do:
live_render(@conn, PlausibleWeb.Live.Verification,
session: %{
"site_id" => @site.id,
"domain" => @site.domain,
"slowdown" => @conn.private[:verification_slowdown],
"flow" => @conn.params["flow"]
}
) %>
<br /> Not working?
<.styled_link
new_tab
href="https://plausible.io/docs/troubleshoot-integration#check-for-the-plausible-snippet-in-your-source-code"
>
Troubleshoot the integration
</.styled_link>
<br />
<span :if={ee?()}>
Check the
<.styled_link href={Routes.site_path(@conn, :settings_general, @site.domain)}>
site settings
</.styled_link>
to invite team members, <br /> import historical stats and more.
</span>
<span :if={ce?()}>
Still not working? Ask on our
<.styled_link new_tab href="https://github.com/plausible/analytics/discussions">
community-supported forum
</.styled_link>
</span>
</p>
</div>
</div>
<%= if Plausible.Verification.enabled?(),
do:
live_render(@conn, PlausibleWeb.Live.Verification,
session: %{
"site_id" => @site.id,
"domain" => @site.domain,
"slowdown" => @conn.private[:verification_slowdown],
"flow" => @conn.params["flow"]
}
) %>

View File

@ -324,7 +324,7 @@ defmodule PlausibleWeb.AuthControllerTest do
describe "GET /login_form" do
test "shows the login form", %{conn: conn} do
conn = get(conn, "/login")
assert html_response(conn, 200) =~ "Enter your email and password"
assert html_response(conn, 200) =~ "Enter your account credentials"
end
end
@ -409,7 +409,7 @@ defmodule PlausibleWeb.AuthControllerTest do
conn = post(conn, "/login", email: "user@example.com", password: "password")
assert get_session(conn, :current_user_id) == nil
assert html_response(conn, 200) =~ "Enter your email and password"
assert html_response(conn, 200) =~ "Enter your account credentials"
end
test "bad password - renders login form again", %{conn: conn} do
@ -417,7 +417,7 @@ defmodule PlausibleWeb.AuthControllerTest do
conn = post(conn, "/login", email: user.email, password: "wrong")
assert get_session(conn, :current_user_id) == nil
assert html_response(conn, 200) =~ "Enter your email and password"
assert html_response(conn, 200) =~ "Enter your account credentials"
end
test "limits login attempts to 5 per minute" do

View File

@ -22,7 +22,7 @@ defmodule PlausibleWeb.SiteControllerTest do
test "shows the site form", %{conn: conn} do
conn = get(conn, "/sites/new")
assert html_response(conn, 200) =~ "Your website details"
assert html_response(conn, 200) =~ "Add website info"
end
test "shows onboarding steps regardless of sites provisioned", %{conn: conn1, user: user} do