purge more passkey and gettext updates

This commit is contained in:
Graham McIntire 2026-02-02 10:20:59 -06:00
parent 02413d5cfd
commit bd6dad85e1
No known key found for this signature in database
16 changed files with 473 additions and 173 deletions

View file

@ -16,8 +16,8 @@ defmodule Towerops.Accounts.LoginAttempt do
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
@valid_methods ~w(password magic_link webauthn mobile_qr)
@valid_failure_reasons ~w(invalid_credentials invalid_totp invalid_magic_link invalid_passkey account_disabled)
@valid_methods ~w(password magic_link mobile_qr)
@valid_failure_reasons ~w(invalid_credentials invalid_totp invalid_magic_link account_disabled)
schema "login_attempts" do
field :email, :string
@ -46,7 +46,7 @@ defmodule Towerops.Accounts.LoginAttempt do
## Required fields
- `:success` - Boolean indicating if login was successful
- `:method` - Login method (password, magic_link, webauthn, mobile_qr)
- `:method` - Login method (password, magic_link, mobile_qr)
- `:ip_address` - IP address of the login attempt
## Conditional validation

View file

@ -1178,7 +1178,6 @@ curl -X DELETE https://towerops.net/api/v1/devices/650e8400-e29b-41d4-a716-44665
<ul class="mt-4 space-y-2 text-sm text-gray-600 dark:text-gray-400 list-disc list-inside">
<li>Profile information (email, name, timezone)</li>
<li>WebAuthn credentials and passkeys</li>
<li>Organizations and membership roles</li>
<li>Devices across all organizations</li>
<li>Recent alerts (last 90 days)</li>

View file

@ -3,41 +3,38 @@
<div class="text-center">
<.header>
<%= if @invitation do %>
Join {@invitation.organization.name}
{t_auth("Join %{organization}", organization: @invitation.organization.name)}
<% else %>
Register for an account
{t_auth("Register for an account")}
<% end %>
<:subtitle>
Already registered?
{t_auth("Already registered?")}
<.link
navigate={~p"/users/log-in"}
class="font-semibold text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
>
Log in
{t_auth("Log in")}
</.link>
to your account now.
{t_auth("to your account now.")}
</:subtitle>
</.header>
<%= if @invitation do %>
<div class="mt-6 rounded-lg bg-blue-50 dark:bg-blue-900/20 p-4 border border-blue-200 dark:border-blue-800">
<p class="text-sm text-blue-900 dark:text-blue-100">
You've been invited to join
<span class="font-semibold">{@invitation.organization.name}</span>
as
a {@invitation.role}.
{t_auth("You've been invited to join %{organization} as a %{role}.", organization: @invitation.organization.name, role: @invitation.role)}
</p>
</div>
<% else %>
<div class="mt-6 rounded-lg bg-blue-50 dark:bg-blue-900/20 p-4 border border-blue-200 dark:border-blue-800">
<p class="text-sm font-medium text-blue-900 dark:text-blue-100">
Free tier includes:
{t_auth("Free tier includes:")}
</p>
<ul class="mt-2 text-sm text-blue-800 dark:text-blue-200 space-y-1">
<li>✓ Monitor up to 10 devices</li>
<li>✓ Real-time alerts and notifications</li>
<li>✓ Performance charts and historical data</li>
<li>✓ No credit card required</li>
<li>{t_auth("✓ Monitor up to 10 devices")}</li>
<li>{t_auth("✓ Real-time alerts and notifications")}</li>
<li>{t_auth("✓ Performance charts and historical data")}</li>
<li>{t_auth("✓ No credit card required")}</li>
</ul>
</div>
<% end %>
@ -52,7 +49,7 @@
<.input
field={f[:email]}
type="email"
label="Email"
label={t_auth("Email")}
autocomplete="email"
required
phx-mounted={JS.focus()}
@ -60,7 +57,7 @@
<.input
field={f[:password]}
type="password"
label="Password"
label={t_auth("Password")}
autocomplete="new-password"
required
/>
@ -70,8 +67,8 @@
name="user[organization_name]"
value=""
type="text"
label="Organization Name"
placeholder="My Company"
label={t_auth("Organization Name")}
placeholder={t_auth("My Company")}
required
/>
<% end %>
@ -89,14 +86,7 @@
</div>
<div class="ml-3 text-sm">
<label for="privacy_policy_consent" class="text-zinc-700 dark:text-zinc-300">
I agree to the
<.link
href={~p"/privacy"}
target="_blank"
class="font-medium text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
>
Privacy Policy
</.link>
{t_auth("I agree to the %{link}", link: raw(~s(<a href="/privacy" target="_blank" class="font-medium text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300">#{t_auth("Privacy Policy")}</a>)))}
<span class="text-red-600 dark:text-red-400">*</span>
</label>
</div>
@ -114,22 +104,15 @@
</div>
<div class="ml-3 text-sm">
<label for="terms_of_service_consent" class="text-zinc-700 dark:text-zinc-300">
I agree to the
<.link
href={~p"/terms"}
target="_blank"
class="font-medium text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
>
Terms of Service
</.link>
{t_auth("I agree to the %{link}", link: raw(~s(<a href="/terms" target="_blank" class="font-medium text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300">#{t_auth("Terms of Service")}</a>)))}
<span class="text-red-600 dark:text-red-400">*</span>
</label>
</div>
</div>
</div>
<.button phx-disable-with="Creating account..." class="w-full mt-6" variant="primary">
{if @invitation, do: "Accept invitation and create account", else: "Create an account"}
<.button phx-disable-with={t_auth("Creating account...")} class="w-full mt-6" variant="primary">
{if @invitation, do: t_auth("Accept invitation and create account"), else: t_auth("Create an account")}
</.button>
</.form>
</div>

View file

@ -2,9 +2,9 @@
<div class="mx-auto max-w-sm">
<div class="text-center">
<.header>
Reset password
{t_auth("Reset password")}
<:subtitle>
Enter your new password below.
{t_auth("Enter your new password below.")}
</:subtitle>
</.header>
</div>
@ -14,7 +14,7 @@
<.input
field={f[:password]}
type="password"
label="New password"
label={t_auth("New password")}
autocomplete="new-password"
required
phx-mounted={JS.focus()}
@ -22,13 +22,13 @@
<.input
field={f[:password_confirmation]}
type="password"
label="Confirm new password"
label={t_auth("Confirm new password")}
autocomplete="new-password"
required
/>
<div class="mt-6">
<.button class="w-full" variant="primary">
Reset password
{t_auth("Reset password")}
</.button>
</div>
</.form>
@ -38,7 +38,7 @@
navigate={~p"/users/log-in"}
class="text-sm font-semibold text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
>
Back to log in
{t_auth("Back to log in")}
</.link>
</div>
</div>

View file

@ -2,9 +2,9 @@
<div class="mx-auto max-w-sm">
<div class="text-center">
<.header>
Forgot your password?
{t_auth("Forgot your password?")}
<:subtitle>
Enter your email address and we'll send you a link to reset your password.
{t_auth("Enter your email address and we'll send you a link to reset your password.")}
</:subtitle>
</.header>
</div>
@ -14,14 +14,14 @@
<.input
field={f[:email]}
type="email"
label="Email"
label={t_auth("Email")}
autocomplete="email"
required
phx-mounted={JS.focus()}
/>
<div class="mt-6">
<.button class="w-full" variant="primary">
Send reset instructions
{t_auth("Send reset instructions")}
</.button>
</div>
</.form>
@ -31,7 +31,7 @@
navigate={~p"/users/log-in"}
class="text-sm font-semibold text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
>
Back to log in
{t_auth("Back to log in")}
</.link>
</div>
</div>

View file

@ -2,19 +2,19 @@
<div class="mx-auto max-w-sm">
<div class="text-center">
<.header>
Log in
{t_auth("Log in")}
<:subtitle>
<%= if @current_scope do %>
You need to reauthenticate to perform sensitive actions on your account.
{t_auth("You need to reauthenticate to perform sensitive actions on your account.")}
<% else %>
Don't have an account?
{t_auth("Don't have an account?")}
<.link
navigate={~p"/users/register"}
class="font-semibold text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
>
Sign up
{t_auth("Sign up")}
</.link>
for an account now.
{t_auth("for an account now.")}
<% end %>
</:subtitle>
</.header>
@ -31,10 +31,10 @@
/>
<div class="ml-3">
<p class="text-sm text-blue-700 dark:text-blue-200">
You are running the local mail adapter.
{t_auth("You are running the local mail adapter.")}
</p>
<p class="mt-1 text-sm text-blue-700 dark:text-blue-200">
To see sent emails, visit <.link href="/dev/mailbox" class="font-medium underline">the mailbox page</.link>.
{t_auth("To see sent emails, visit %{link}.", link: raw(~s(<a href="/dev/mailbox" class="font-medium underline">#{t_auth("the mailbox page")}</a>)))}
</p>
</div>
</div>
@ -47,7 +47,7 @@
readonly={!!@current_scope}
field={f[:email]}
type="email"
label="Email"
label={t_auth("Email")}
autocomplete="email"
required
phx-mounted={JS.focus()}
@ -55,7 +55,7 @@
<.input
field={f[:password]}
type="password"
label="Password"
label={t_auth("Password")}
autocomplete="current-password"
required
/>
@ -64,12 +64,12 @@
navigate={~p"/users/reset-password"}
class="text-sm font-semibold text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
>
Forgot password?
{t_auth("Forgot password?")}
</.link>
</div>
<div class="space-y-3">
<.button class="w-full" variant="primary" name={@form[:remember_me].name} value="true">
Log in <span aria-hidden="true">→</span>
{t_auth("Log in")} <span aria-hidden="true">→</span>
</.button>
</div>
</.form>
@ -80,42 +80,25 @@
</div>
<div class="relative flex justify-center text-sm">
<span class="bg-gray-50 px-2 text-gray-600 dark:bg-gray-950 dark:text-gray-400">
or
{t("or")}
</span>
</div>
</div>
<!-- Alternative: Email Login Link -->
<.form :let={f} for={@form} as={:user} id="login_form_magic" action={~p"/users/log-in"}>
<.input
readonly={!!@current_scope}
field={f[:email]}
type="email"
label="Email"
label={t_auth("Email")}
autocomplete="email"
required
/>
<.button class="w-full">
Send me a login link instead
{t_auth("Send me a login link instead")}
</.button>
</.form>
<!-- Passkey Login (Discoverable) -->
<div>
<div
id="passkey-discoverable-error"
class="hidden mb-3 rounded-lg bg-red-50 p-3 dark:bg-red-900/20"
>
<p class="text-sm text-red-800 dark:text-red-200"></p>
</div>
<button
type="button"
id="passkey-discoverable-btn"
class="w-full inline-flex items-center justify-center gap-2 rounded-lg bg-gray-100 px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm hover:bg-gray-200 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600 disabled:opacity-50 disabled:cursor-not-allowed dark:bg-white/10 dark:text-white dark:hover:bg-white/20"
>
<.icon name="hero-key" class="h-5 w-5" /> Log in with passkey
</button>
</div>
</div>
</div>
</Layouts.app>

View file

@ -118,7 +118,7 @@
</p>
</div>
<div class="md:col-span-2" phx-update="replace" id="mikrotik-config-section">
<div class="md:col-span-2">
<.input
field={@form[:mikrotik_enabled]}
type="checkbox"

View file

@ -2,7 +2,7 @@ defmodule ToweropsWeb.UserSettingsLive do
@moduledoc """
LiveView for managing user account settings.
Allows users to update email, password, manage passkeys, and mobile sessions.
Allows users to update email, password, and mobile sessions.
"""
use ToweropsWeb, :live_view

View file

@ -1583,78 +1583,4 @@
</div>
</div>
<% end %>
<!-- Passkey Registration Modal -->
<div
id="passkey-modal"
class="hidden fixed inset-0 z-50 overflow-y-auto"
aria-labelledby="modal-title"
role="dialog"
aria-modal="true"
>
<div class="flex min-h-screen items-end justify-center px-4 pb-20 pt-4 text-center sm:block sm:p-0">
<div
class="fixed inset-0 z-0 bg-gray-500 bg-opacity-75 transition-opacity dark:bg-gray-950 dark:bg-opacity-75"
aria-hidden="true"
>
</div>
<span
class="relative z-10 hidden sm:inline-block sm:h-screen sm:align-middle"
aria-hidden="true"
>
&#8203;
</span>
<div class="relative z-10 inline-block transform overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left align-bottom shadow-xl transition-all dark:bg-gray-800/95 sm:my-8 sm:w-full sm:max-w-lg sm:p-6 sm:align-middle">
<div>
<div class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-900">
<.icon name="hero-key" class="h-6 w-6 text-blue-600 dark:text-blue-400" />
</div>
<div class="mt-3 text-center sm:mt-5">
<h3
class="text-lg font-semibold leading-6 text-gray-900 dark:text-white"
id="add-passkey-modal-title"
>
Add Passkey
</h3>
<div class="mt-2">
<p class="text-sm text-gray-500 dark:text-gray-400">
Give this passkey a name to help you identify it later (e.g., "MacBook Touch ID", "iPhone").
</p>
</div>
<div class="mt-4">
<input
type="text"
id="passkey-name-input"
placeholder="e.g., MacBook Touch ID"
class="block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-white/10 dark:bg-gray-800/50 dark:text-white sm:text-sm"
/>
</div>
<div
id="passkey-error"
class="mt-3 hidden rounded-lg bg-red-50 p-3 dark:bg-red-900/20"
>
<p class="text-sm text-red-800 dark:text-red-200"></p>
</div>
</div>
</div>
<div class="mt-5 sm:mt-6 sm:grid sm:grid-flow-row-dense sm:grid-cols-2 sm:gap-3">
<button
type="button"
id="confirm-add-passkey"
class="inline-flex w-full justify-center rounded-lg bg-blue-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 disabled:opacity-50 disabled:cursor-not-allowed sm:col-start-2"
>
Continue
</button>
<button
type="button"
id="cancel-add-passkey"
class="mt-3 inline-flex w-full justify-center rounded-lg bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:bg-gray-800/50 dark:text-white dark:ring-white/10 dark:hover:bg-gray-800 sm:col-start-1 sm:mt-0"
>
Cancel
</button>
</div>
</div>
</div>
</div>
</Layouts.authenticated>

View file

@ -82,7 +82,6 @@ defmodule ToweropsWeb.UserSettingsLive.Helpers do
case method do
"password" -> {"hero-lock-closed", "Password"}
"magic_link" -> {"hero-envelope", "Magic Link"}
"webauthn" -> {"hero-key", "Passkey"}
"mobile_qr" -> {"hero-device-phone-mobile", "Mobile QR"}
_ -> {"hero-question-mark-circle", method}
end

View file

@ -13,3 +13,202 @@
msgid ""
msgstr ""
"Language: en\n"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:115
#, elixir-autogen, elixir-format
msgid "Accept invitation and create account"
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:11
#, elixir-autogen, elixir-format
msgid "Already registered?"
msgstr ""
#: lib/towerops_web/controllers/user_reset_password_html/edit.html.heex:41
#: lib/towerops_web/controllers/user_reset_password_html/new.html.heex:34
#, elixir-autogen, elixir-format
msgid "Back to log in"
msgstr ""
#: lib/towerops_web/controllers/user_reset_password_html/edit.html.heex:25
#, elixir-autogen, elixir-format
msgid "Confirm new password"
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:115
#, elixir-autogen, elixir-format
msgid "Create an account"
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:114
#, elixir-autogen, elixir-format
msgid "Creating account..."
msgstr ""
#: lib/towerops_web/controllers/user_session_html/new.html.heex:10
#, elixir-autogen, elixir-format
msgid "Don't have an account?"
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:52
#: lib/towerops_web/controllers/user_reset_password_html/new.html.heex:17
#: lib/towerops_web/controllers/user_session_html/new.html.heex:50
#: lib/towerops_web/controllers/user_session_html/new.html.heex:94
#, elixir-autogen, elixir-format
msgid "Email"
msgstr ""
#: lib/towerops_web/controllers/user_reset_password_html/new.html.heex:7
#, elixir-autogen, elixir-format
msgid "Enter your email address and we'll send you a link to reset your password."
msgstr ""
#: lib/towerops_web/controllers/user_reset_password_html/edit.html.heex:7
#, elixir-autogen, elixir-format
msgid "Enter your new password below."
msgstr ""
#: lib/towerops_web/controllers/user_session_html/new.html.heex:67
#, elixir-autogen, elixir-format
msgid "Forgot password?"
msgstr ""
#: lib/towerops_web/controllers/user_reset_password_html/new.html.heex:5
#, elixir-autogen, elixir-format
msgid "Forgot your password?"
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:31
#, elixir-autogen, elixir-format
msgid "Free tier includes:"
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:89
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:107
#, elixir-autogen, elixir-format
msgid "I agree to the %{link}"
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:6
#, elixir-autogen, elixir-format
msgid "Join %{organization}"
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:16
#: lib/towerops_web/controllers/user_session_html/new.html.heex:5
#: lib/towerops_web/controllers/user_session_html/new.html.heex:72
#, elixir-autogen, elixir-format
msgid "Log in"
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:71
#, elixir-autogen, elixir-format
msgid "My Company"
msgstr ""
#: lib/towerops_web/controllers/user_reset_password_html/edit.html.heex:17
#, elixir-autogen, elixir-format
msgid "New password"
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:70
#, elixir-autogen, elixir-format
msgid "Organization Name"
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:60
#: lib/towerops_web/controllers/user_session_html/new.html.heex:58
#, elixir-autogen, elixir-format
msgid "Password"
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:89
#, elixir-autogen, elixir-format
msgid "Privacy Policy"
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:8
#, elixir-autogen, elixir-format
msgid "Register for an account"
msgstr ""
#: lib/towerops_web/controllers/user_reset_password_html/edit.html.heex:5
#: lib/towerops_web/controllers/user_reset_password_html/edit.html.heex:31
#, elixir-autogen, elixir-format
msgid "Reset password"
msgstr ""
#: lib/towerops_web/controllers/user_session_html/new.html.heex:99
#, elixir-autogen, elixir-format
msgid "Send me a login link instead"
msgstr ""
#: lib/towerops_web/controllers/user_reset_password_html/new.html.heex:24
#, elixir-autogen, elixir-format
msgid "Send reset instructions"
msgstr ""
#: lib/towerops_web/controllers/user_session_html/new.html.heex:15
#, elixir-autogen, elixir-format
msgid "Sign up"
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:107
#, elixir-autogen, elixir-format
msgid "Terms of Service"
msgstr ""
#: lib/towerops_web/controllers/user_session_html/new.html.heex:37
#, elixir-autogen, elixir-format
msgid "To see sent emails, visit %{link}."
msgstr ""
#: lib/towerops_web/controllers/user_session_html/new.html.heex:34
#, elixir-autogen, elixir-format
msgid "You are running the local mail adapter."
msgstr ""
#: lib/towerops_web/controllers/user_session_html/new.html.heex:8
#, elixir-autogen, elixir-format
msgid "You need to reauthenticate to perform sensitive actions on your account."
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:25
#, elixir-autogen, elixir-format
msgid "You've been invited to join %{organization} as a %{role}."
msgstr ""
#: lib/towerops_web/controllers/user_session_html/new.html.heex:17
#, elixir-autogen, elixir-format
msgid "for an account now."
msgstr ""
#: lib/towerops_web/controllers/user_session_html/new.html.heex:37
#, elixir-autogen, elixir-format
msgid "the mailbox page"
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:18
#, elixir-autogen, elixir-format
msgid "to your account now."
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:34
#, elixir-autogen, elixir-format
msgid "✓ Monitor up to 10 devices"
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:37
#, elixir-autogen, elixir-format
msgid "✓ No credit card required"
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:36
#, elixir-autogen, elixir-format
msgid "✓ Performance charts and historical data"
msgstr ""
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:35
#, elixir-autogen, elixir-format
msgid "✓ Real-time alerts and notifications"
msgstr ""

View file

@ -86,3 +86,8 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "close"
msgstr ""
#: lib/towerops_web/controllers/user_session_html/new.html.heex:83
#, elixir-autogen, elixir-format
msgid "or"
msgstr ""

View file

@ -10,3 +10,202 @@ msgid ""
msgstr ""
"Language: en\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:115
#, elixir-autogen, elixir-format
msgid "Accept invitation and create account"
msgstr "Accept invitation and create account"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:11
#, elixir-autogen, elixir-format
msgid "Already registered?"
msgstr "Already registered?"
#: lib/towerops_web/controllers/user_reset_password_html/edit.html.heex:41
#: lib/towerops_web/controllers/user_reset_password_html/new.html.heex:34
#, elixir-autogen, elixir-format
msgid "Back to log in"
msgstr "Back to log in"
#: lib/towerops_web/controllers/user_reset_password_html/edit.html.heex:25
#, elixir-autogen, elixir-format
msgid "Confirm new password"
msgstr "Confirm new password"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:115
#, elixir-autogen, elixir-format
msgid "Create an account"
msgstr "Create an account"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:114
#, elixir-autogen, elixir-format
msgid "Creating account..."
msgstr "Creating account..."
#: lib/towerops_web/controllers/user_session_html/new.html.heex:10
#, elixir-autogen, elixir-format
msgid "Don't have an account?"
msgstr "Don't have an account?"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:52
#: lib/towerops_web/controllers/user_reset_password_html/new.html.heex:17
#: lib/towerops_web/controllers/user_session_html/new.html.heex:50
#: lib/towerops_web/controllers/user_session_html/new.html.heex:94
#, elixir-autogen, elixir-format
msgid "Email"
msgstr "Email"
#: lib/towerops_web/controllers/user_reset_password_html/new.html.heex:7
#, elixir-autogen, elixir-format
msgid "Enter your email address and we'll send you a link to reset your password."
msgstr "Enter your email address and we'll send you a link to reset your password."
#: lib/towerops_web/controllers/user_reset_password_html/edit.html.heex:7
#, elixir-autogen, elixir-format
msgid "Enter your new password below."
msgstr "Enter your new password below."
#: lib/towerops_web/controllers/user_session_html/new.html.heex:67
#, elixir-autogen, elixir-format
msgid "Forgot password?"
msgstr "Forgot password?"
#: lib/towerops_web/controllers/user_reset_password_html/new.html.heex:5
#, elixir-autogen, elixir-format
msgid "Forgot your password?"
msgstr "Forgot your password?"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:31
#, elixir-autogen, elixir-format
msgid "Free tier includes:"
msgstr "Free tier includes:"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:89
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:107
#, elixir-autogen, elixir-format
msgid "I agree to the %{link}"
msgstr "I agree to the %{link}"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:6
#, elixir-autogen, elixir-format
msgid "Join %{organization}"
msgstr "Join %{organization}"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:16
#: lib/towerops_web/controllers/user_session_html/new.html.heex:5
#: lib/towerops_web/controllers/user_session_html/new.html.heex:72
#, elixir-autogen, elixir-format
msgid "Log in"
msgstr "Log in"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:71
#, elixir-autogen, elixir-format
msgid "My Company"
msgstr "My Company"
#: lib/towerops_web/controllers/user_reset_password_html/edit.html.heex:17
#, elixir-autogen, elixir-format
msgid "New password"
msgstr "New password"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:70
#, elixir-autogen, elixir-format
msgid "Organization Name"
msgstr "Organization Name"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:60
#: lib/towerops_web/controllers/user_session_html/new.html.heex:58
#, elixir-autogen, elixir-format
msgid "Password"
msgstr "Password"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:89
#, elixir-autogen, elixir-format
msgid "Privacy Policy"
msgstr "Privacy Policy"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:8
#, elixir-autogen, elixir-format
msgid "Register for an account"
msgstr "Register for an account"
#: lib/towerops_web/controllers/user_reset_password_html/edit.html.heex:5
#: lib/towerops_web/controllers/user_reset_password_html/edit.html.heex:31
#, elixir-autogen, elixir-format
msgid "Reset password"
msgstr "Reset password"
#: lib/towerops_web/controllers/user_session_html/new.html.heex:99
#, elixir-autogen, elixir-format
msgid "Send me a login link instead"
msgstr "Send me a login link instead"
#: lib/towerops_web/controllers/user_reset_password_html/new.html.heex:24
#, elixir-autogen, elixir-format
msgid "Send reset instructions"
msgstr "Send reset instructions"
#: lib/towerops_web/controllers/user_session_html/new.html.heex:15
#, elixir-autogen, elixir-format
msgid "Sign up"
msgstr "Sign up"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:107
#, elixir-autogen, elixir-format
msgid "Terms of Service"
msgstr "Terms of Service"
#: lib/towerops_web/controllers/user_session_html/new.html.heex:37
#, elixir-autogen, elixir-format
msgid "To see sent emails, visit %{link}."
msgstr "To see sent emails, visit %{link}."
#: lib/towerops_web/controllers/user_session_html/new.html.heex:34
#, elixir-autogen, elixir-format
msgid "You are running the local mail adapter."
msgstr "You are running the local mail adapter."
#: lib/towerops_web/controllers/user_session_html/new.html.heex:8
#, elixir-autogen, elixir-format
msgid "You need to reauthenticate to perform sensitive actions on your account."
msgstr "You need to reauthenticate to perform sensitive actions on your account."
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:25
#, elixir-autogen, elixir-format
msgid "You've been invited to join %{organization} as a %{role}."
msgstr "You've been invited to join %{organization} as a %{role}."
#: lib/towerops_web/controllers/user_session_html/new.html.heex:17
#, elixir-autogen, elixir-format
msgid "for an account now."
msgstr "for an account now."
#: lib/towerops_web/controllers/user_session_html/new.html.heex:37
#, elixir-autogen, elixir-format
msgid "the mailbox page"
msgstr "the mailbox page"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:18
#, elixir-autogen, elixir-format
msgid "to your account now."
msgstr "to your account now."
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:34
#, elixir-autogen, elixir-format
msgid "✓ Monitor up to 10 devices"
msgstr "✓ Monitor up to 10 devices"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:37
#, elixir-autogen, elixir-format
msgid "✓ No credit card required"
msgstr "✓ No credit card required"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:36
#, elixir-autogen, elixir-format
msgid "✓ Performance charts and historical data"
msgstr "✓ Performance charts and historical data"
#: lib/towerops_web/controllers/user_registration_html/new.html.heex:35
#, elixir-autogen, elixir-format
msgid "✓ Real-time alerts and notifications"
msgstr "✓ Real-time alerts and notifications"

View file

@ -86,3 +86,8 @@ msgstr "We can't find the internet"
#, elixir-autogen, elixir-format
msgid "close"
msgstr "close"
#: lib/towerops_web/controllers/user_session_html/new.html.heex:83
#, elixir-autogen, elixir-format
msgid "or"
msgstr "or"

View file

@ -19,7 +19,6 @@ defmodule ToweropsWeb.UserSessionHTMLTest do
assert html =~ "Log in"
assert html =~ "Sign up"
assert html =~ "Log in with passkey"
assert html =~ "Send me a login link instead"
# Password login is now the default (no longer separated by "or use password")
assert html =~ "Password"

View file

@ -62,11 +62,12 @@ defmodule ToweropsWeb.Org.SettingsLiveTest do
|> log_in_user(user)
|> live(~p"/orgs/#{org.slug}/settings")
assert view
|> form("#organization-form", organization: %{name: "Updated Name"})
|> render_submit()
html =
view
|> form("#organization-form", organization: %{name: "Updated Name"})
|> render_submit()
assert_redirected(view, ~p"/orgs/#{org.slug}")
assert html =~ "Settings saved successfully"
# Verify the organization was actually updated
updated_org = Towerops.Organizations.get_organization!(org.id)
@ -81,11 +82,12 @@ defmodule ToweropsWeb.Org.SettingsLiveTest do
|> log_in_user(user)
|> live(~p"/orgs/#{org.slug}/settings")
assert view
|> form("#organization-form", organization: %{default_agent_token_id: agent_token.id})
|> render_submit()
html =
view
|> form("#organization-form", organization: %{default_agent_token_id: agent_token.id})
|> render_submit()
assert_redirected(view, ~p"/orgs/#{org.slug}")
assert html =~ "Settings saved successfully"
# Verify the default agent was set
updated_org = Towerops.Organizations.get_organization!(org.id)
@ -107,11 +109,12 @@ defmodule ToweropsWeb.Org.SettingsLiveTest do
|> live(~p"/orgs/#{org.slug}/settings")
# Now clear it
assert view
|> form("#organization-form", organization: %{default_agent_token_id: ""})
|> render_submit()
html =
view
|> form("#organization-form", organization: %{default_agent_token_id: ""})
|> render_submit()
assert_redirected(view, ~p"/orgs/#{org.slug}")
assert html =~ "Settings saved successfully"
# Verify the default agent was cleared
updated_org = Towerops.Organizations.get_organization!(org.id)