analytics/lib/plausible_web/templates/auth/user_settings.html.heex
Vinicius Brasil 7f51928338
Remove business tier feature flag (#3632)
* Remove business tier feature flag

This commit removes all code branches related to the business tier
feature flag, as we're not flipping this flag off anymore. It also
removes unused routes, e.g. /billing/change-plan and /billing/upgrade

* remove unused billing templates

* refactor with clause to case instead

* assert on the url in email tests

---------

Co-authored-by: Robert Joonas <robertjoonas16@gmail.com>
2023-12-14 11:25:46 -03:00

499 lines
21 KiB
Plaintext

<div x-data="{disable2FAOpen: false, regenerate2FAOpen: false}">
<%= if full_build?() do %>
<div class="max-w-2xl px-8 pt-4 pb-8 mx-auto mt-24 bg-white border-t-2 border-orange-200 rounded rounded-t-none shadow-md dark:bg-gray-800 dark:border-orange-200 ">
<div class="flex flex-wrap justify-between">
<h2 class="text-xl font-black dark:text-gray-100 w-max mr-4 mt-2">Subscription Plan</h2>
<div class="gap-x-2 mt-2 inline-flex">
<span
:if={@subscription && Plausible.Billing.Plans.business_tier?(@subscription)}
class={[
"w-max px-2.5 py-0.5 rounded-md text-sm font-bold leading-5 text-indigo-600 bg-blue-100 dark:text-yellow-200 dark:border dark:bg-inherit dark:border-yellow-200"
]}
>
Business
</span>
<span
:if={@subscription}
class={[
"w-max px-2.5 py-0.5 rounded-md text-sm font-bold leading-5",
subscription_colors(@subscription.status)
]}
>
<%= present_subscription_status(@subscription.status) %>
</span>
</div>
</div>
<div class="my-4 border-b border-gray-400"></div>
<PlausibleWeb.Components.Billing.subscription_cancelled_notice
user={@user}
dismissable={false}
/>
<div class="flex flex-col items-center justify-between mt-8 sm:flex-row sm:items-start">
<PlausibleWeb.Components.Billing.monthly_quota_box
user={@user}
subscription={@subscription}
/>
<div
class="h-32 px-2 py-4 my-4 text-center bg-gray-100 rounded dark:bg-gray-900"
style="width: 11.75rem;"
>
<h4 class="font-black dark:text-gray-100">Next bill amount</h4>
<%= if Plausible.Billing.Subscription.Status.in?(@subscription, [Plausible.Billing.Subscription.Status.active(), Plausible.Billing.Subscription.Status.past_due()]) do %>
<div class="py-2 text-xl font-medium dark:text-gray-100">
<%= PlausibleWeb.BillingView.present_currency(@subscription.currency_code) %><%= @subscription.next_bill_amount %>
</div>
<.styled_link :if={@subscription.update_url} href={@subscription.update_url}>
Update billing info
</.styled_link>
<% else %>
<div class="py-2 text-xl font-medium dark:text-gray-100">---</div>
<% end %>
</div>
<div
class="h-32 px-2 py-4 my-4 text-center bg-gray-100 rounded dark:bg-gray-900"
style="width: 11.75rem;"
>
<h4 class="font-black dark:text-gray-100">Next bill date</h4>
<%= if @subscription && @subscription.next_bill_date && Plausible.Billing.Subscription.Status.in?(@subscription, [Plausible.Billing.Subscription.Status.active(), Plausible.Billing.Subscription.Status.past_due()]) do %>
<div class="py-2 text-xl font-medium dark:text-gray-100">
<%= Timex.format!(@subscription.next_bill_date, "{Mshort} {D}, {YYYY}") %>
</div>
<span class="text-gray-600 dark:text-gray-400">
(<%= subscription_interval(@subscription) %> billing)
</span>
<% else %>
<div class="py-2 text-xl font-medium dark:text-gray-100">---</div>
<% end %>
</div>
</div>
<PlausibleWeb.Components.Billing.render_monthly_pageview_usage
usage={@pageview_usage}
limit={@pageview_limit}
/>
<article class="mt-8">
<h1 class="text-xl mb-3 font-bold dark:text-gray-100">Sites & team members usage</h1>
<PlausibleWeb.Components.Billing.usage_and_limits_table>
<PlausibleWeb.Components.Billing.usage_and_limits_row
id="site-usage-row"
title="Owned sites"
usage={@site_usage}
limit={@site_limit}
/>
<PlausibleWeb.Components.Billing.usage_and_limits_row
id="team-member-usage-row"
title="Team members"
usage={@team_member_usage}
limit={@team_member_limit}
/>
</PlausibleWeb.Components.Billing.usage_and_limits_table>
</article>
<%= cond do %>
<% Plausible.Billing.Subscriptions.resumable?(@subscription) && @subscription.cancel_url -> %>
<div class="mt-8">
<%= link("Cancel my subscription",
to: @subscription.cancel_url,
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 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150"
) %>
</div>
<% true -> %>
<div class="mt-8">
<PlausibleWeb.Components.Billing.upgrade_link user={@user} />
</div>
<% end %>
</div>
<%= case @invoices do %>
<% {:error, :no_invoices} -> %>
<% {:error, :request_failed} -> %>
<div class="max-w-2xl px-8 pt-6 pb-8 mx-auto mt-16 bg-white border-t-2 border-orange-200 rounded rounded-t-none shadow-md dark:bg-gray-800">
<h2 class="text-xl font-black dark:text-gray-100">Invoices</h2>
<div class="my-4 border-b border-gray-300 dark:border-gray-500"></div>
<p class="text-center text-black dark:text-gray-100 m-2">
Something went wrong
</p>
</div>
<% {:ok, invoice_list} when is_list(invoice_list) -> %>
<div class="max-w-2xl px-8 pt-6 pb-8 mx-auto mt-16 bg-white border-t-2 border-orange-200 rounded rounded-t-none shadow-md dark:bg-gray-800">
<h2 class="text-xl font-black dark:text-gray-100">Invoices</h2>
<div class="my-4 border-b border-gray-300 dark:border-gray-500"></div>
<table class="min-w-full divide-y divide-gray-200">
<thead>
<tr>
<th
scope="col"
class="py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider"
>
Date
</th>
<th
scope="col"
class="py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider"
>
Amount
</th>
<th
scope="col"
class="py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider"
>
Invoice
</th>
</tr>
</thead>
<%= for invoice <- format_invoices(invoice_list) do %>
<tbody class="divide-y divide-gray-200">
<tr>
<td class="py-4 text-sm text-gray-800 dark:text-gray-200 font-medium">
<%= invoice.date %>
</td>
<td class="py-4 text-sm text-gray-800 dark:text-gray-200">
<%= invoice.currency <> invoice.amount %>
</td>
<td class="py-4 text-sm text-indigo-500">
<%= link("Link", to: invoice.url, target: "_blank") %>
</td>
</tr>
</tbody>
<% end %>
</table>
</div>
<% end %>
<% end %>
<div class="max-w-2xl px-8 pt-6 pb-8 mx-auto mt-16 bg-white border-t-2 border-green-500 rounded rounded-t-none shadow-md dark:bg-gray-800">
<h2 class="text-xl font-black dark:text-gray-100">Dashboard Appearance</h2>
<div class="my-4 border-b border-gray-300 dark:border-gray-500"></div>
<%= form_for @settings_changeset, "/settings", [class: "max-w-sm"], fn f -> %>
<div class="col-span-4 sm:col-span-2">
<%= label(f, :theme, "Theme Selection",
class: "block text-sm font-medium leading-5 text-gray-700 dark:text-gray-300"
) %>
<%= select(f, :theme, Plausible.Themes.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"
) %>
</div>
<PlausibleWeb.Components.Generic.button type="submit" class="mt-4">
Save
</PlausibleWeb.Components.Generic.button>
<% end %>
</div>
<div class="max-w-2xl px-8 pt-6 pb-8 mx-auto mt-16 bg-white border-t-2 border-green-500 rounded rounded-t-none shadow-md dark:bg-gray-800">
<h2 id="setup-2fa" class="text-xl font-black dark:text-gray-100">
Two-Factor Authentication (2FA)
</h2>
<p class="text-sm mt-2 text-gray-600 dark:text-gray-400">
Two-Factor Authentication protects your account by adding an extra security step when you log in.
</p>
<%= if @totp_enabled? do %>
<button
x-on:click="disable2FAOpen = true; $refs.disable2FAPassword.value = ''"
type="button"
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"
>
Disable 2FA
</button>
<p class="mt-2 text-gray-600 text-sm dark:text-gray-400">
Lost your recovery codes?
<a
href="#setup-2fa"
x-on:click="regenerate2FAOpen = true; $refs.regenerate2FAPassword.value = ''"
class="underline text-indigo-600"
>
Generate new
</a>
</p>
<% else %>
<%= form_for @conn.params, Routes.auth_path(@conn, :initiate_2fa_setup), fn _ -> %>
<PlausibleWeb.Components.Generic.button type="submit" class="mt-4">
Enable 2FA
</PlausibleWeb.Components.Generic.button>
<% end %>
<% end %>
</div>
<div class="max-w-2xl px-8 pt-6 pb-8 mx-auto mt-16 bg-white border-t-2 border-indigo-100 rounded rounded-t-none shadow-md dark:bg-gray-800 dark:border-indigo-500">
<h2 id="change-account-name" class="text-xl font-black dark:text-gray-100">
Change account name
</h2>
<div class="my-4 border-b border-gray-300 dark:border-gray-500"></div>
<%= form_for @settings_changeset, "/settings#change-account-name", [class: "max-w-sm"], fn f -> %>
<div class="my-4">
<%= label(f, :name, class: "block text-sm font-medium text-gray-700 dark:text-gray-300") %>
<div class="mt-1">
<%= text_input(f, :name,
class:
"shadow-sm dark:bg-gray-900 dark:text-gray-300 focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:bg-gray-800"
) %>
<%= error_tag(f, :name) %>
</div>
</div>
<PlausibleWeb.Components.Generic.button type="submit" class="mt-4">
Save
</PlausibleWeb.Components.Generic.button>
<% end %>
</div>
<div class="max-w-2xl px-8 pt-6 pb-8 mx-auto mt-16 bg-white border-t-2 border-red-600 rounded rounded-t-none shadow-md dark:bg-gray-800">
<h2 id="change-email-address" class="text-xl font-black dark:text-gray-100">
Change email address
</h2>
<div class="my-4 border-b border-gray-300 dark:border-gray-500"></div>
<%= form_for @email_changeset, "/settings/email#change-email-address", [class: "max-w-sm"], fn f -> %>
<div class="my-4">
<%= label(f, :password, "Account password",
class: "block text-sm font-medium text-gray-700 dark:text-gray-300"
) %>
<div class="mt-1">
<%= password_input(f, :password,
class:
"shadow-sm dark:bg-gray-900 dark:text-gray-300 focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:bg-gray-800"
) %>
<%= error_tag(f, :password) %>
</div>
</div>
<div class="my-4">
<%= label(f, :current_email, "Current email",
class: "block text-sm font-medium text-gray-700 dark:text-gray-300"
) %>
<div class="mt-1">
<%= email_input(f, :current_email,
readonly: true,
value: f.data.email,
class:
"shadow-sm dark:bg-gray-900 dark:text-gray-300 focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:bg-gray-800 bg-gray-100"
) %>
</div>
</div>
<div class="my-4">
<%= label(f, :email, "New email",
class: "block text-sm font-medium text-gray-700 dark:text-gray-300"
) %>
<div class="mt-1">
<%= email_input(f, :email,
value: f.params["email"],
class:
"shadow-sm dark:bg-gray-900 dark:text-gray-300 focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md"
) %>
<%= error_tag(f, :email) %>
</div>
</div>
<%= submit("Change my email",
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"
) %>
<% end %>
</div>
<div
id="api-keys"
class="max-w-2xl px-8 pt-6 pb-8 mx-auto mt-16 bg-white border-t-2 border-indigo-100 rounded rounded-t-none shadow-md dark:bg-gray-800 dark:border-indigo-500"
>
<h2 class="text-xl font-black dark:text-gray-100">API Keys</h2>
<div class="my-4 border-b border-gray-300 dark:border-gray-500"></div>
<PlausibleWeb.Components.Billing.premium_feature_notice
billable_user={@current_user}
current_user={@current_user}
feature_mod={Plausible.Billing.Feature.StatsAPI}
/>
<div class="flex flex-col mt-6">
<div class="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
<%= if Enum.any?(@user.api_keys) do %>
<div class="overflow-hidden border-b border-gray-200 shadow dark:border-gray-900 sm:rounded-lg">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-900">
<thead class="bg-gray-50 dark:bg-gray-900">
<tr>
<th
scope="col"
class="px-6 py-3 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-gray-100"
>
Name
</th>
<th
scope="col"
class="px-6 py-3 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-gray-100"
>
Key
</th>
<th scope="col" class="relative px-6 py-3">
<span class="sr-only">Revoke</span>
</th>
</tr>
</thead>
<tbody>
<%= for api_key <- @user.api_keys do %>
<tr class="bg-white dark:bg-gray-800">
<td class="px-6 py-4 text-sm font-medium text-gray-900 dark:text-gray-100 whitespace-nowrap">
<%= api_key.name %>
</td>
<td class="px-6 py-4 text-sm text-gray-500 dark:text-gray-100 whitespace-nowrap">
<%= api_key.key_prefix %><%= String.duplicate("*", 32 - 6) %>
</td>
<td class="px-6 py-4 text-sm font-medium text-right whitespace-nowrap">
<%= button("Revoke",
to: "/settings/api-keys/#{api_key.id}",
class: "text-red-600 hover:text-red-900",
method: :delete,
"data-confirm":
"Are you sure you want to revoke this key? This action cannot be reversed."
) %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% end %>
<PlausibleWeb.Components.Generic.button_link
:if={Plausible.Billing.Feature.StatsAPI.check_availability(@current_user) == :ok}
href={Routes.auth_path(@conn, :new_api_key)}
class="mt-4"
>
+ New API Key
</PlausibleWeb.Components.Generic.button_link>
</div>
</div>
</div>
</div>
<div class="max-w-2xl px-8 pt-6 pb-8 mx-auto mt-16 mb-24 bg-white border-t-2 border-red-600 rounded rounded-t-none shadow-md dark:bg-gray-800">
<div class="flex items-center justify-between">
<h2 class="text-xl font-black dark:text-gray-100">Delete account</h2>
</div>
<div class="my-4 border-b border-gray-300 dark:border-gray-500"></div>
<p class="dark:text-gray-100">
Deleting your account removes all sites and stats you've collected
</p>
<%= if Plausible.Billing.Subscription.Status.active?(@subscription) do %>
<span class="mt-6 bg-gray-300 button dark:bg-gray-600 hover:shadow-none hover:bg-gray-300 cursor-not-allowed">
Delete my account
</span>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
Your account cannot be deleted because you have an active subscription. If you want to delete your account, please cancel your subscription first.
</p>
<% else %>
<%= link("Delete my account",
to: "/me",
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",
method: "delete",
data: [
confirm:
"Deleting your account will also delete all the sites and data that you own. This action cannot be reversed. Are you sure?"
]
) %>
<% end %>
</div>
<PlausibleWeb.Components.TwoFactor.modal
:let={f}
id="disable-2fa-modal"
state_param="disable2FAOpen"
form_data={@conn.params}
form_target={Routes.auth_path(@conn, :disable_2fa)}
title="Disable Two-Factor Authentication?"
>
<:icon>
<Heroicons.shield_exclamation class="h-6 w-6" />
</:icon>
<:buttons>
<.button type="submit" class="w-full sm:w-auto">
Disable 2FA
</.button>
</:buttons>
<div class="text-sm mt-2">
Once disabled, verification codes from the authenticator application and current recovery codes will become invalid. 2FA will have to be setup from the start.
</div>
<div class="text-sm mt-2">
Enter your password to disable 2FA.
</div>
<div class="mt-3">
<%= password_input(f, :password,
id: "disable_2fa_password",
placeholder: "Enter password",
"x-ref": "disable2FAPassword",
class:
"shadow-sm dark:bg-gray-900 dark:text-gray-300 focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:bg-gray-800"
) %>
</div>
</PlausibleWeb.Components.TwoFactor.modal>
<PlausibleWeb.Components.TwoFactor.modal
:let={f}
id="regenerate-2fa-modal"
state_param="regenerate2FAOpen"
form_data={@conn.params}
form_target={Routes.auth_path(@conn, :generate_2fa_recovery_codes)}
onsubmit="document.getElementById('generate-2fa-recovery-button').disabled = true"
title="Generate New Recovery Codes?"
>
<:icon>
<Heroicons.key class="h-6 w-6" />
</:icon>
<:buttons>
<.button
id="generate-2fa-recovery-button"
type="submit"
class="w-full sm:w-auto [&>span.label-enabled]:block [&>span.label-disabled]:hidden [&[disabled]>span.label-enabled]:hidden [&[disabled]>span.label-disabled]:block"
>
<span class="label-enabled pointer-events-none">
Generate New Codes
</span>
<span class="label-disabled">
<.spinner class="inline-block h-5 w-5 mr-2 text-white dark:text-gray-400" />
Generating Codes
</span>
</.button>
</:buttons>
<div class="text-sm mt-2">
If you generate new codes, the old ones will become invalid.
</div>
<div class="text-sm mt-2">
Enter your password to continue.
</div>
<div class="mt-3 w-full">
<%= password_input(f, :password,
id: "regenerate_2fa_password",
placeholder: "Enter password",
"x-ref": "regenerate2FAPassword",
class:
"w-full shadow-sm dark:bg-gray-900 dark:text-gray-300 focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:bg-gray-800"
) %>
</div>
</PlausibleWeb.Components.TwoFactor.modal>
</div>