diff --git a/lib/phoenix_kit/migrations/postgres.ex b/lib/phoenix_kit/migrations/postgres.ex index a69eaaea8..df30ce2bf 100644 --- a/lib/phoenix_kit/migrations/postgres.ex +++ b/lib/phoenix_kit/migrations/postgres.ex @@ -529,7 +529,13 @@ defmodule PhoenixKit.Migrations.Postgres do - Replaces unique index with partial index (slug-mode only, WHERE slug IS NOT NULL) - Adds unique index on `(group_uuid, post_date, post_time)` for timestamp-mode posts - ### V146 - Catalogue item primary supplier ⚡ LATEST + ### V147 - Known-device geo-location ⚡ LATEST + - Adds nullable `location` (`City, Country`) to + `phoenix_kit_user_known_devices`. Resolved once at new-device time by + `PhoenixKit.Users.LoginAlerts` and stored so the user's Active Sessions + list can show sign-in location without a per-render geo lookup. + + ### V146 - Catalogue item primary supplier - Adds nullable `primary_supplier_uuid` FK (`ON DELETE SET NULL`) + partial index to `phoenix_kit_cat_items` — an item's default supplier, independent of manufacturer (generic/unbranded materials; @@ -1282,7 +1288,7 @@ defmodule PhoenixKit.Migrations.Postgres do alias PhoenixKit.Migrations.Postgres.Helpers @initial_version 1 - @current_version 146 + @current_version 147 @default_prefix "public" # First version whose SQL references uuid_generate_v7(). Chains that diff --git a/lib/phoenix_kit/migrations/postgres/v147.ex b/lib/phoenix_kit/migrations/postgres/v147.ex new file mode 100644 index 000000000..f1b6aab5e --- /dev/null +++ b/lib/phoenix_kit/migrations/postgres/v147.ex @@ -0,0 +1,38 @@ +defmodule PhoenixKit.Migrations.Postgres.V147 do + @moduledoc """ + V147: Persist geo-location on known devices. + + Adds a nullable `location` column to `phoenix_kit_user_known_devices`. The + "City, Country" string is already resolved at new-device time by + `PhoenixKit.Users.LoginAlerts` (it was only used in the alert email); + storing it lets the user's Active Sessions list show where each session + signed in from without an extra geo lookup per page render. + """ + + use Ecto.Migration + + def up(opts) do + prefix = Map.get(opts, :prefix, "public") + p = prefix_str(prefix) + + alter table(:phoenix_kit_user_known_devices, prefix: prefix) do + add_if_not_exists(:location, :string, size: 255) + end + + execute("COMMENT ON TABLE #{p}phoenix_kit IS '147'") + end + + def down(opts) do + prefix = Map.get(opts, :prefix, "public") + p = prefix_str(prefix) + + alter table(:phoenix_kit_user_known_devices, prefix: prefix) do + remove_if_exists(:location, :string) + end + + execute("COMMENT ON TABLE #{p}phoenix_kit IS '146'") + end + + defp prefix_str("public"), do: "public." + defp prefix_str(prefix), do: "#{prefix}." +end diff --git a/lib/phoenix_kit/notifications/types.ex b/lib/phoenix_kit/notifications/types.ex index 8dd901046..5203cd274 100644 --- a/lib/phoenix_kit/notifications/types.ex +++ b/lib/phoenix_kit/notifications/types.ex @@ -83,6 +83,13 @@ defmodule PhoenixKit.Notifications.Types do defp core_types do [ + %{ + key: "security", + label: "Security", + description: "New sign-ins to your account from an unrecognized device", + actions: ["user.new_login_detected"], + default: true + }, %{ key: "account", label: "Account", diff --git a/lib/phoenix_kit/users/auth/known_device.ex b/lib/phoenix_kit/users/auth/known_device.ex index abaa923f2..e3d589e18 100644 --- a/lib/phoenix_kit/users/auth/known_device.ex +++ b/lib/phoenix_kit/users/auth/known_device.ex @@ -24,6 +24,7 @@ defmodule PhoenixKit.Users.Auth.KnownDevice do field :user_agent_hash, :string field :browser, :string field :os, :string + field :location, :string field :first_seen_at, :utc_datetime field :last_seen_at, :utc_datetime @@ -39,6 +40,7 @@ defmodule PhoenixKit.Users.Auth.KnownDevice do :user_agent_hash, :browser, :os, + :location, :first_seen_at, :last_seen_at ]) diff --git a/lib/phoenix_kit/users/login_alerts.ex b/lib/phoenix_kit/users/login_alerts.ex index a493678ee..5beb7a774 100644 --- a/lib/phoenix_kit/users/login_alerts.ex +++ b/lib/phoenix_kit/users/login_alerts.ex @@ -23,11 +23,15 @@ defmodule PhoenixKit.Users.LoginAlerts do require Logger + use Gettext, backend: PhoenixKitWeb.Gettext + + alias PhoenixKit.Notifications alias PhoenixKit.RepoHelper alias PhoenixKit.Settings alias PhoenixKit.Users.Auth.KnownDevice alias PhoenixKit.Users.Auth.UserNotifier alias PhoenixKit.Utils.Geolocation + alias PhoenixKit.Utils.Routes alias PhoenixKit.Utils.SessionFingerprint alias PhoenixKit.Utils.UserAgent @@ -83,6 +87,9 @@ defmodule PhoenixKit.Users.LoginAlerts do user_agent_hash: fingerprint.user_agent_hash, browser: UserAgent.browser(ua), os: UserAgent.os(ua), + # Resolved once here and persisted (V147) so the Active Sessions list + # can show it later without re-hitting the geo API per page render. + location: location_for(fingerprint.ip_address), first_seen_at: now, last_seen_at: now } @@ -95,12 +102,45 @@ defmodule PhoenixKit.Users.LoginAlerts do ) log_new_login(user, attrs) + notify_in_app(user, attrs) - email_attrs = Map.put(attrs, :location, location_for(fingerprint.ip_address)) - UserNotifier.deliver_new_login_alert(user, email_attrs) + UserNotifier.deliver_new_login_alert(user, attrs) :ok end + # In-app notification for the new sign-in. The `user.new_login_detected` + # activity is self-actor (actor == target), so the activity→notification + # hook correctly skips it — this is the sanctioned standalone path for an + # app-driven self-notice, filtered through the recipient's "security" + # type preference (fail-open). Links to the Active Sessions section. + defp notify_in_app(user, attrs) do + if Code.ensure_loaded?(Notifications) do + Notifications.create(%{ + recipient_uuid: user.uuid, + type: "security", + icon: "hero-shield-exclamation", + link: Routes.path("/dashboard/settings"), + text: new_login_text(attrs) + }) + end + rescue + error -> + Logger.warning("[PhoenixKit.LoginAlerts] in-app notify failed: #{inspect(error)}") + :ok + end + + defp new_login_text(attrs) do + details = + [attrs.browser, attrs.os, attrs.location] + |> Enum.reject(&(is_nil(&1) or &1 == "")) + |> Enum.join(", ") + + case details do + "" -> gettext("New sign-in to your account.") + _ -> gettext("New sign-in to your account from %{details}.", details: details) + end + end + defp log_new_login(user, attrs) do if Code.ensure_loaded?(PhoenixKit.Activity) do PhoenixKit.Activity.log(%{ diff --git a/lib/phoenix_kit/users/qr_login.ex b/lib/phoenix_kit/users/qr_login.ex index 7ce5c733c..2676eb27f 100644 --- a/lib/phoenix_kit/users/qr_login.ex +++ b/lib/phoenix_kit/users/qr_login.ex @@ -37,6 +37,7 @@ defmodule PhoenixKit.Users.QrLogin do alias Phoenix.LiveView alias PhoenixKit.Settings + alias PhoenixKit.Utils.Geolocation alias PhoenixKit.Utils.IpAddress alias PhoenixKit.Utils.UserAgent @@ -88,20 +89,56 @@ defmodule PhoenixKit.Users.QrLogin do socket, shown verbatim on the phone confirm screen so the human can recognise (or reject) the sign-in. - Keys: `:browser`, `:os`, `:ip` — any of which may be absent when the - underlying connect-info is unavailable. + Keys: `:browser`, `:os`, `:ip`, `:location` — any of which may be absent + when the underlying connect-info (or geo lookup) is unavailable — plus + `:requested_at`, an absolute UTC timestamp of when the code was minted so + the approver can sanity-check "did I just do this?". + + The geo lookup is a best-effort, timeout-bounded call on the requesting + browser's IP (same machinery registration/login-alerts already use); it + runs at QR-mint time so the location is baked into the request the phone + later reads. """ @spec device_meta(LiveView.Socket.t()) :: map() def device_meta(socket) do ua = LiveView.get_connect_info(socket, :user_agent) - ip = IpAddress.extract_from_socket(socket) + # extract_from_socket/1 returns the literal "unknown" when peer_data is + # unavailable (proxies, some transports) — treat that (and blanks) as + # absent so the confirm screen omits the IP row instead of showing a + # bare "unknown", and so we don't feed a placeholder into the geo lookup. + ip = present_ip(IpAddress.extract_from_socket(socket)) - %{} + %{requested_at: requested_at()} |> put_present(:browser, UserAgent.browser(ua)) |> put_present(:os, UserAgent.os(ua)) |> put_present(:ip, ip) + |> put_present(:location, ip && location_for(ip)) + end + + @doc """ + Formats a best-effort `"City, Country"` (or just `"Country"`) string for + an IP, or `nil` when the lookup fails or is unavailable. Never raises — a + geo backend hiccup must not crash the QR mint that shows the code. + """ + @spec location_for(String.t() | nil) :: String.t() | nil + def location_for(ip) when is_binary(ip) do + case Geolocation.lookup_location(ip) do + {:ok, %{"city" => city, "country" => country}} + when is_binary(city) and city != "" and is_binary(country) -> + "#{city}, #{country}" + + {:ok, %{"country" => country}} when is_binary(country) and country != "" -> + country + + _ -> + nil + end + rescue + _ -> nil end + def location_for(_), do: nil + ## ── Activity logging ─────────────────────────────────────────────────── @doc """ @@ -139,4 +176,13 @@ defmodule PhoenixKit.Users.QrLogin do defp put_present(map, _key, nil), do: map defp put_present(map, _key, ""), do: map defp put_present(map, key, value), do: Map.put(map, key, value) + + # Placeholder IPs from `IpAddress.extract_from_socket/1` (unreadable peer + # data) count as "no IP" so they neither render nor drive a geo lookup. + defp present_ip(ip) when ip in [nil, "", "unknown"], do: nil + defp present_ip(ip), do: ip + + defp requested_at do + Calendar.strftime(DateTime.utc_now(), "%Y-%m-%d %H:%M UTC") + end end diff --git a/lib/phoenix_kit/users/sessions.ex b/lib/phoenix_kit/users/sessions.ex index 4c11ab72d..a80828188 100644 --- a/lib/phoenix_kit/users/sessions.ex +++ b/lib/phoenix_kit/users/sessions.ex @@ -24,9 +24,10 @@ defmodule PhoenixKit.Users.Sessions do """ import Ecto.Query, warn: false + require Logger alias PhoenixKit.Admin.Events alias PhoenixKit.RepoHelper, as: Repo - alias PhoenixKit.Users.Auth.{User, UserToken} + alias PhoenixKit.Users.Auth.{KnownDevice, User, UserToken} alias PhoenixKit.Utils.Date, as: UtilsDate @session_validity_in_days 60 @@ -105,6 +106,95 @@ defmodule PhoenixKit.Users.Sessions do |> Enum.map(&format_session_info/1) end + @doc """ + Lists a user's active sessions enriched with device info, for the + self-service "Active Sessions" UI. + + Each session's `(ip_address, user_agent_hash)` is matched against the + user's `KnownDevice` history to recover browser/OS/location/last-active + (session tokens store only the hashed UA, never the raw string). Sessions + predating fingerprinting — or from a device never recorded as "known" — + degrade gracefully to an "Unknown device" with nil fields. + + `current_token` is the raw session token of the browser making the + request (from the session's `"user_token"`); the matching row is flagged + `is_current: true` so the UI can mark it and omit its "Sign out" button. + """ + def list_user_device_sessions(%User{uuid: user_uuid}, current_token) do + known = known_devices_by_fingerprint(user_uuid) + current_uuid = current_session_uuid(user_uuid, current_token) + + from(token in UserToken, + where: token.context == "session", + where: token.user_uuid == ^user_uuid, + where: token.inserted_at > ago(@session_validity_in_days, "day"), + select: %{ + token_uuid: token.uuid, + ip_address: token.ip_address, + user_agent_hash: token.user_agent_hash, + created_at: token.inserted_at + }, + order_by: [desc: token.inserted_at] + ) + |> Repo.all() + |> Enum.map(fn s -> + device = Map.get(known, {s.ip_address, s.user_agent_hash}) + + %{ + token_uuid: s.token_uuid, + ip_address: s.ip_address, + browser: device && device.browser, + os: device && device.os, + location: device && device.location, + last_active: (device && device.last_seen_at) || s.created_at, + created_at: s.created_at, + is_current: s.token_uuid == current_uuid + } + end) + end + + @doc """ + Revokes one of a user's *own* sessions by token uuid. + + Scoped to `user` so a user can never revoke another user's session by + guessing a token uuid. Returns `:ok` or `{:error, :not_found}`. + """ + def revoke_user_session(%User{uuid: user_uuid}, token_uuid) when is_binary(token_uuid) do + case Repo.delete_all( + from(t in UserToken, + where: t.uuid == ^token_uuid and t.user_uuid == ^user_uuid and t.context == "session" + ) + ) do + {1, _} -> + Events.broadcast_session_revoked(token_uuid) + :ok + + {0, _} -> + {:error, :not_found} + end + end + + @doc """ + Revokes all of a user's sessions except the one identified by + `current_token` (kept so the acting browser stays signed in). Returns the + number revoked. With a nil token, revokes every session for the user. + """ + def revoke_other_user_sessions(%User{} = user, nil), do: revoke_user_sessions(user) + + def revoke_other_user_sessions(%User{uuid: user_uuid}, current_token) + when is_binary(current_token) do + {count, _} = + Repo.delete_all( + from(t in UserToken, + where: + t.user_uuid == ^user_uuid and t.context == "session" and t.token != ^current_token + ) + ) + + if count > 0, do: Events.broadcast_user_sessions_revoked(user_uuid, count) + count + end + @doc """ Gets detailed information about a specific session by token ID. @@ -271,6 +361,39 @@ defmodule PhoenixKit.Users.Sessions do } end + # Loads the user's known devices keyed by {ip_address, user_agent_hash} + # for O(1) enrichment of each session row. + # + # Degrades to no enrichment (empty map) if the known-devices table isn't + # present yet — a parent app can deploy code carrying this feature before + # running the V143/V147 migrations, and the sessions list (built from the + # tokens table) must still render rather than crash the settings page. + defp known_devices_by_fingerprint(user_uuid) do + from(d in KnownDevice, where: d.user_uuid == ^user_uuid) + |> Repo.all() + |> Map.new(fn d -> {{d.ip_address, d.user_agent_hash}, d} end) + rescue + error in [Postgrex.Error, DBConnection.ConnectionError] -> + Logger.warning( + "[PhoenixKit.Sessions] known-device enrichment skipped " <> + "(run PhoenixKit migrations to V147?): #{inspect(error)}" + ) + + %{} + end + + # Resolves the token uuid of the acting session (session tokens are stored + # raw, so a direct byte match is correct). Nil token / no match → nil. + defp current_session_uuid(_user_uuid, nil), do: nil + + defp current_session_uuid(user_uuid, token) when is_binary(token) do + from(t in UserToken, + where: t.context == "session" and t.user_uuid == ^user_uuid and t.token == ^token, + select: t.uuid + ) + |> Repo.one() + end + # Private helper to format session information defp format_session_info(session_data) do %{ diff --git a/lib/phoenix_kit_web/live/components/user_settings.ex b/lib/phoenix_kit_web/live/components/user_settings.ex index c220bc2c5..fa4a778da 100644 --- a/lib/phoenix_kit_web/live/components/user_settings.ex +++ b/lib/phoenix_kit_web/live/components/user_settings.ex @@ -20,11 +20,15 @@ defmodule PhoenixKitWeb.Live.Components.UserSettings do ## Optional assigns - * `sections` — list of sections to display: `:identity`, `:custom_fields`, `:email`, `:password`, `:oauth`, `:notifications` - (default: all six). `:profile` is accepted as a legacy alias that expands to `[:identity, :custom_fields]` + * `sections` — list of sections to display: `:identity`, `:custom_fields`, `:email`, `:password`, `:oauth`, `:notifications`, `:sessions` + (default: all). `:profile` is accepted as a legacy alias that expands to `[:identity, :custom_fields]` * `email_confirm_url_fn` — `(token -> url)` for email confirmation links (default: `&Routes.url("/dashboard/settings/confirm-email/\#{&1}")`) * `return_to` — where OAuth redirect returns to (default: `"/dashboard/settings"`) + * `current_session_token` — raw session token of the acting browser, used + by the `:sessions` section to mark the current device and to keep it + signed in on "sign out other sessions". Without it, no session is + flagged current and "sign out others" revokes every session. ## Parent notifications @@ -42,9 +46,18 @@ defmodule PhoenixKitWeb.Live.Components.UserSettings do alias PhoenixKit.Users.CustomFields alias PhoenixKit.Users.OAuth alias PhoenixKit.Users.OAuthAvailability + alias PhoenixKit.Users.Sessions alias PhoenixKit.Utils.Routes - @default_sections [:identity, :custom_fields, :email, :password, :oauth, :notifications] + @default_sections [ + :identity, + :custom_fields, + :email, + :password, + :oauth, + :notifications, + :sessions + ] @impl true def update(%{action: :set_avatar, file_uuid: file_uuid}, socket) do @@ -152,6 +165,13 @@ defmodule PhoenixKitWeb.Live.Components.UserSettings do |> assign_new(:notification_types, fn -> NotificationTypes.list() end) |> assign_new(:notification_prefs, fn -> NotificationPrefs.get(user) end) |> assign_new(:notification_success_message, fn -> nil end) + |> assign_new(:current_session_token, fn -> assigns[:current_session_token] end) + |> assign_new(:session_success_message, fn -> nil end) + + socket = + assign_new(socket, :sessions, fn -> + load_sessions(socket.assigns.user, socket.assigns.current_session_token) + end) {:ok, socket} end @@ -475,8 +495,59 @@ defmodule PhoenixKitWeb.Live.Components.UserSettings do end end + def handle_event("revoke_session", %{"uuid" => token_uuid}, socket) do + user = socket.assigns.user + + message = + case Sessions.revoke_user_session(user, token_uuid) do + :ok -> gettext("Signed out of that session.") + {:error, :not_found} -> gettext("That session is no longer active.") + end + + {:noreply, + socket + |> assign(:sessions, load_sessions(user, socket.assigns.current_session_token)) + |> assign(:session_success_message, message)} + end + + def handle_event("revoke_other_sessions", _params, socket) do + user = socket.assigns.user + Sessions.revoke_other_user_sessions(user, socket.assigns.current_session_token) + + {:noreply, + socket + |> assign(:sessions, load_sessions(user, socket.assigns.current_session_token)) + |> assign(:session_success_message, gettext("Signed out of all other sessions."))} + end + # Private helpers + defp load_sessions(user, current_token) do + Sessions.list_user_device_sessions(user, current_token) + end + + defp device_label(%{browser: b, os: o}) when is_binary(b) and is_binary(o), do: "#{b} · #{o}" + defp device_label(%{browser: b}) when is_binary(b), do: b + defp device_label(%{os: o}) when is_binary(o), do: o + defp device_label(_), do: gettext("Unknown device") + + defp session_meta_line(session) do + [session.location, session.ip_address, last_active_label(session)] + |> Enum.reject(&(is_nil(&1) or &1 == "")) + |> Enum.join(" · ") + end + + defp last_active_label(%{is_current: true}), do: gettext("Active now") + defp last_active_label(%{last_active: nil}), do: nil + + defp last_active_label(%{last_active: last_active}) do + case DateTime.diff(DateTime.utc_now(), last_active, :day) do + days when days <= 0 -> gettext("Active today") + 1 -> gettext("Active yesterday") + days -> gettext("Active %{count} days ago", count: days) + end + end + defp check_timezone_mismatch(socket, selected_timezone) do browser_offset = socket.assigns[:browser_timezone_offset] browser_name = socket.assigns[:browser_timezone_name] @@ -1164,6 +1235,78 @@ defmodule PhoenixKitWeb.Live.Components.UserSettings do <% end %> + + <%!-- Active Sessions Section --%> + <%= if :sessions in @sections do %> + <%= if Enum.any?([:identity, :custom_fields, :email, :password, :oauth, :notifications], & &1 in @sections) do %> +
+ <% end %> +
+

+ <.icon name="hero-computer-desktop" class="w-5 h-5 text-primary" /> + {gettext("Active Sessions")} +

+ + <%= if @session_success_message do %> +
+ <.icon name="hero-check" class="stroke-current shrink-0 h-4 w-4" /> + {@session_success_message} +
+ <% end %> + +

+ {gettext( + "Devices currently signed in to your account. If you don't recognize one, sign it out." + )} +

+ +
+ <%= for session <- @sessions do %> +
+
+ <.icon + name="hero-computer-desktop" + class="w-5 h-5 mt-0.5 shrink-0 text-base-content/50" + /> +
+
+ {device_label(session)} + + {gettext("This device")} + +
+
+ {session_meta_line(session)} +
+
+
+ +
+ <% end %> +
+ +
0} class="flex justify-end pt-3"> + +
+
+ <% end %> """ diff --git a/lib/phoenix_kit_web/live/dashboard/settings.ex b/lib/phoenix_kit_web/live/dashboard/settings.ex index 5bcd48d3c..6b4071353 100644 --- a/lib/phoenix_kit_web/live/dashboard/settings.ex +++ b/lib/phoenix_kit_web/live/dashboard/settings.ex @@ -30,10 +30,13 @@ defmodule PhoenixKitWeb.Live.Dashboard.Settings do end @impl true - def mount(_params, _session, socket) do + def mount(_params, session, socket) do socket = socket |> assign(:page_title, gettext("Settings")) + # Raw session token of this browser — lets the Active Sessions section + # mark the current device and keep it signed in on "sign out others". + |> assign(:current_session_token, session["user_token"]) |> assign_new(:email_success_message, fn -> nil end) |> assign_new(:email_error_message, fn -> nil end) @@ -59,6 +62,7 @@ defmodule PhoenixKitWeb.Live.Dashboard.Settings do module={PhoenixKitWeb.Live.Components.UserSettings} id="dashboard-user-settings" user={@phoenix_kit_current_user} + current_session_token={@current_session_token} email_success_message={@email_success_message} email_error_message={@email_error_message} /> diff --git a/lib/phoenix_kit_web/users/qr_login.ex b/lib/phoenix_kit_web/users/qr_login.ex index 9c2fdf1b2..a4d704298 100644 --- a/lib/phoenix_kit_web/users/qr_login.ex +++ b/lib/phoenix_kit_web/users/qr_login.ex @@ -24,12 +24,17 @@ defmodule PhoenixKitWeb.Users.QrLogin do # the server-side one. @request_ttl_ms :timer.minutes(2) - def mount(_params, _session, socket) do + def mount(params, _session, socket) do case Auth.maybe_redirect_authenticated(socket) do {:redirect, socket} -> {:ok, socket} :cont -> + # `return_to` is a post-login destination the browser carries through + # the whole handoff (mint → approve → finish); sanitized against open + # redirects up front. remember_me defaults off, toggled on this page. + socket = assign(socket, :return_to, sanitize_return_to(params["return_to"])) + cond do not QrLoginContext.enabled?() -> {:ok, @@ -82,8 +87,29 @@ defmodule PhoenixKitWeb.Users.QrLogin do def handle_event("keyfob_refresh", _params, socket), do: {:noreply, socket |> Keyfob.Live.refresh() |> schedule_expiry()} + # "Keep me logged in" checkbox. A daisyUI checkbox in a phx-change form + # sends "on" when ticked and omits the key when unticked. + def handle_event("set_remember", params, socket), + do: {:noreply, assign(socket, :remember_me, params["remember_me"] == "on")} + + # On approval, hand off to the completion controller carrying the browser's + # remember_me / return_to choice (the login token is minted on the phone, + # but these belong to the browser being signed in). defp complete(socket, login_token) do - {:noreply, redirect(socket, to: Routes.path("/users/qr-login/finish/#{login_token}"))} + query = + [ + {"remember_me", if(socket.assigns[:remember_me], do: "true")}, + {"return_to", socket.assigns[:return_to]} + ] + |> Enum.reject(fn {_k, v} -> is_nil(v) end) + + base = Routes.path("/users/qr-login/finish/#{login_token}") + to = if query == [], do: base, else: base <> "?" <> URI.encode_query(query) + {:noreply, redirect(socket, to: to)} + end + + defp sanitize_return_to(path) do + if Routes.local_path?(path), do: path, else: nil end # Every connect to this public, pre-auth page mints a live keyfob request @@ -113,7 +139,9 @@ defmodule PhoenixKitWeb.Users.QrLogin do defp confirm_url(token), do: Routes.url("/users/qr-login/scan/#{token}") defp assign_common(socket) do - assign(socket, :project_title, PhoenixKit.Settings.get_project_title()) + socket + |> assign(:project_title, PhoenixKit.Settings.get_project_title()) + |> assign_new(:remember_me, fn -> false end) end defp panel_labels do diff --git a/lib/phoenix_kit_web/users/qr_login.html.heex b/lib/phoenix_kit_web/users/qr_login.html.heex index ceab01f7a..f66819d0b 100644 --- a/lib/phoenix_kit_web/users/qr_login.html.heex +++ b/lib/phoenix_kit_web/users/qr_login.html.heex @@ -23,6 +23,18 @@ +
+ +
+
  1. 1. {gettext("Open the camera on your signed-in phone.")}
  2. 2. {gettext("Point it at the code above.")}
  3. diff --git a/lib/phoenix_kit_web/users/qr_login_complete.ex b/lib/phoenix_kit_web/users/qr_login_complete.ex index eef1b907b..23832239b 100644 --- a/lib/phoenix_kit_web/users/qr_login_complete.ex +++ b/lib/phoenix_kit_web/users/qr_login_complete.ex @@ -18,13 +18,16 @@ defmodule PhoenixKitWeb.Users.QrLoginComplete do alias PhoenixKit.Utils.Routes alias PhoenixKitWeb.Users.Auth, as: UserAuth - def complete(conn, %{"token" => token}) do + def complete(conn, %{"token" => token} = params) do with true <- QrLoginContext.enabled?(), {:ok, user_uuid} <- QrLoginContext.consume(token), %{} = user <- Users.get_user(user_uuid) do conn + # log_in_user/3 reads :user_return_to from the session for its redirect, + # so stash the (sanitized) destination there before signing in. + |> maybe_store_return_to(params["return_to"]) |> put_flash(:info, gettext("Signed in with QR code.")) - |> UserAuth.log_in_user(user) + |> UserAuth.log_in_user(user, login_params(params)) else _ -> conn @@ -38,4 +41,17 @@ defmodule PhoenixKitWeb.Users.QrLoginComplete do |> put_flash(:error, gettext("This QR sign-in link is invalid or has expired.")) |> redirect(to: Routes.path("/users/log-in")) end + + # Only "true" (from the browser's checkbox) opts into the persistent + # remember-me cookie; anything else keeps a session-only login. + defp login_params(%{"remember_me" => "true"}), do: %{"remember_me" => "true"} + defp login_params(_params), do: %{} + + defp maybe_store_return_to(conn, return_to) do + if is_binary(return_to) and Routes.local_path?(return_to) do + put_session(conn, :user_return_to, return_to) + else + conn + end + end end diff --git a/priv/gettext/de/LC_MESSAGES/default.po b/priv/gettext/de/LC_MESSAGES/default.po index c9120fc88..3066cb099 100644 --- a/priv/gettext/de/LC_MESSAGES/default.po +++ b/priv/gettext/de/LC_MESSAGES/default.po @@ -168,6 +168,7 @@ msgstr "" msgid "Registered accounts" msgstr "" +#: lib/phoenix_kit_web/live/components/user_settings.ex:1247 #: lib/phoenix_kit_web/live/dashboard.html.heex:170 #: lib/phoenix_kit_web/live/dashboard.html.heex:175 #: lib/phoenix_kit_web/live/users/sessions.html.heex:18 @@ -427,7 +428,7 @@ msgstr "" msgid "%{language} (Published)" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:383 +#: lib/phoenix_kit_web/live/components/user_settings.ex:403 #, elixir-autogen, elixir-format msgid "%{provider} account disconnected successfully" msgstr "" @@ -447,7 +448,7 @@ msgstr "" msgid "123 Business Street" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:196 +#: lib/phoenix_kit_web/live/components/user_settings.ex:216 #, elixir-autogen, elixir-format msgid "A link to confirm your email change has been sent to the new address." msgstr "" @@ -668,12 +669,12 @@ msgstr "" msgid "Cannot delete your own account" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:417 +#: lib/phoenix_kit_web/live/components/user_settings.ex:437 #, elixir-autogen, elixir-format msgid "Cannot disconnect %{provider}. Please ensure you have at least one sign-in method available." msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:412 +#: lib/phoenix_kit_web/live/components/user_settings.ex:432 #, elixir-autogen, elixir-format msgid "Cannot disconnect %{provider}. This is your only sign-in method. Please set a password or connect another provider first." msgstr "" @@ -1079,7 +1080,7 @@ msgstr "" msgid "Failed to delete user" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:403 +#: lib/phoenix_kit_web/live/components/user_settings.ex:423 #, elixir-autogen, elixir-format msgid "Failed to disconnect provider. Please try again." msgstr "" @@ -1275,6 +1276,7 @@ msgid "Italic" msgstr "" #: lib/phoenix_kit_web/users/login.html.heex:58 +#: lib/phoenix_kit_web/users/qr_login.html.heex:34 #, elixir-autogen, elixir-format msgid "Keep me logged in" msgstr "" @@ -1532,7 +1534,7 @@ msgstr "Seite erfolgreich veröffentlicht" msgid "Password" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:247 +#: lib/phoenix_kit_web/live/components/user_settings.ex:267 #, elixir-autogen, elixir-format msgid "Password changed successfully." msgstr "" @@ -1614,7 +1616,7 @@ msgstr "Datenschutzeinstellungen" msgid "Profile" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:319 +#: lib/phoenix_kit_web/live/components/user_settings.ex:339 #, elixir-autogen, elixir-format msgid "Profile updated successfully" msgstr "" @@ -1630,7 +1632,7 @@ msgstr "" msgid "Protected core roles" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:393 +#: lib/phoenix_kit_web/live/components/user_settings.ex:413 #, elixir-autogen, elixir-format msgid "Provider not found" msgstr "" @@ -3945,7 +3947,7 @@ msgstr "" msgid "Authorize access to your %{provider} account. This will open a sign-in page where you choose which account to connect." msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:76 +#: lib/phoenix_kit_web/live/components/user_settings.ex:89 #, elixir-autogen, elixir-format, fuzzy msgid "Avatar updated successfully!" msgstr "" @@ -4380,7 +4382,7 @@ msgstr "" msgid "Failed to save" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:473 +#: lib/phoenix_kit_web/live/components/user_settings.ex:493 #, elixir-autogen, elixir-format msgid "Failed to save notification preferences." msgstr "" @@ -4400,7 +4402,7 @@ msgstr "Einstellungen konnten nicht gespeichert werden" msgid "Failed to toggle maintenance mode" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:83 +#: lib/phoenix_kit_web/live/components/user_settings.ex:96 #, elixir-autogen, elixir-format, fuzzy msgid "Failed to update avatar" msgstr "" @@ -4661,7 +4663,7 @@ msgstr "" msgid "Maintenance schedule saved" msgstr "" -#: lib/phoenix_kit_web/live/dashboard/settings.ex:55 +#: lib/phoenix_kit_web/live/dashboard/settings.ex:58 #, elixir-autogen, elixir-format msgid "Manage your account settings and preferences" msgstr "" @@ -4825,12 +4827,12 @@ msgstr "" msgid "Not tested" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:466 +#: lib/phoenix_kit_web/live/components/user_settings.ex:486 #, elixir-autogen, elixir-format msgid "Notification preferences saved." msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1113 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1184 #: lib/phoenix_kit_web/live/modules.html.heex:667 #: lib/phoenix_kit_web/live/modules/notifications/index.html.heex:5 #: lib/phoenix_kit_web/live/settings.html.heex:318 @@ -4916,7 +4918,7 @@ msgstr "" msgid "Person" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1124 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1195 #, elixir-autogen, elixir-format msgid "Pick which notification types you want to receive. Unchecked types are muted — activities still record in the audit log but no bell notification is created for you." msgstr "" @@ -5007,7 +5009,7 @@ msgstr "" msgid "Save Tax Settings" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1161 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1232 #, elixir-autogen, elixir-format, fuzzy msgid "Save preferences" msgstr "Einstellungen speichern" @@ -10141,12 +10143,12 @@ msgstr "" msgid "Approve this sign-in?" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:125 +#: lib/phoenix_kit_web/users/qr_login.ex:153 #, elixir-autogen, elixir-format msgid "Approved — signing you in…" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:37 +#: lib/phoenix_kit_web/users/qr_login.html.heex:49 #, elixir-autogen, elixir-format msgid "Back to sign in" msgstr "" @@ -10215,12 +10217,12 @@ msgstr "" msgid "Only approve if this is you. Approving signs that browser in to your account." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:27 +#: lib/phoenix_kit_web/users/qr_login.html.heex:39 #, elixir-autogen, elixir-format msgid "Open the camera on your signed-in phone." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:28 +#: lib/phoenix_kit_web/users/qr_login.html.heex:40 #, elixir-autogen, elixir-format msgid "Point it at the code above." msgstr "" @@ -10235,7 +10237,7 @@ msgstr "" msgid "QR Code" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:37 +#: lib/phoenix_kit_web/users/qr_login.ex:42 #: lib/phoenix_kit_web/users/qr_login_confirm.ex:32 #: lib/phoenix_kit_web/users/qr_login_confirm.ex:76 #, elixir-autogen, elixir-format @@ -10252,12 +10254,12 @@ msgstr "" msgid "Requested" msgstr "Erforderlich" -#: lib/phoenix_kit_web/users/qr_login.ex:122 +#: lib/phoenix_kit_web/users/qr_login.ex:150 #, elixir-autogen, elixir-format msgid "Scan this code with your phone's camera, then approve the sign-in on your phone." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:128 +#: lib/phoenix_kit_web/users/qr_login.ex:156 #, elixir-autogen, elixir-format msgid "Show a new code" msgstr "" @@ -10274,28 +10276,28 @@ msgstr "" msgid "Signed in" msgstr "" -#: lib/phoenix_kit_web/users/qr_login_complete.ex:26 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:29 #, elixir-autogen, elixir-format msgid "Signed in with QR code." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:29 +#: lib/phoenix_kit_web/users/qr_login.html.heex:41 #, elixir-autogen, elixir-format msgid "Tap Approve on your phone to finish signing in here." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:126 +#: lib/phoenix_kit_web/users/qr_login.ex:154 #, elixir-autogen, elixir-format msgid "The sign-in request was denied." msgstr "" -#: lib/phoenix_kit_web/users/qr_login_complete.ex:31 -#: lib/phoenix_kit_web/users/qr_login_complete.ex:38 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:34 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:41 #, elixir-autogen, elixir-format msgid "This QR sign-in link is invalid or has expired." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:127 +#: lib/phoenix_kit_web/users/qr_login.ex:155 #, elixir-autogen, elixir-format msgid "This code expired." msgstr "" @@ -10384,7 +10386,7 @@ msgstr "" msgid "Or add via" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:56 +#: lib/phoenix_kit_web/users/qr_login.ex:61 #, elixir-autogen, elixir-format msgid "Too many attempts. Please try again shortly." msgstr "" @@ -10498,3 +10500,78 @@ msgstr "" #, elixir-autogen, elixir-format msgid "xAI requires a funded account before the API will return completions" msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:547 +#, elixir-autogen, elixir-format +msgid "Active %{count} days ago" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:540 +#, elixir-autogen, elixir-format, fuzzy +msgid "Active now" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:545 +#, elixir-autogen, elixir-format, fuzzy +msgid "Active today" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:546 +#, elixir-autogen, elixir-format +msgid "Active yesterday" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1258 +#, elixir-autogen, elixir-format +msgid "Devices currently signed in to your account. If you don't recognize one, sign it out." +msgstr "" + +#: lib/phoenix_kit/users/login_alerts.ex:140 +#, elixir-autogen, elixir-format +msgid "New sign-in to your account from %{details}." +msgstr "" + +#: lib/phoenix_kit/users/login_alerts.ex:139 +#, elixir-autogen, elixir-format +msgid "New sign-in to your account." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1291 +#, elixir-autogen, elixir-format, fuzzy +msgid "Sign out" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1302 +#, elixir-autogen, elixir-format +msgid "Sign out of all other sessions?" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1305 +#, elixir-autogen, elixir-format +msgid "Sign out other sessions" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:520 +#, elixir-autogen, elixir-format +msgid "Signed out of all other sessions." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:503 +#, elixir-autogen, elixir-format +msgid "Signed out of that session." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:504 +#, elixir-autogen, elixir-format +msgid "That session is no longer active." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1275 +#, elixir-autogen, elixir-format +msgid "This device" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:532 +#, elixir-autogen, elixir-format, fuzzy +msgid "Unknown device" +msgstr "" diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot index 7ec8ee7c1..3350f1a0c 100644 --- a/priv/gettext/default.pot +++ b/priv/gettext/default.pot @@ -166,6 +166,7 @@ msgstr "" msgid "Registered accounts" msgstr "" +#: lib/phoenix_kit_web/live/components/user_settings.ex:1247 #: lib/phoenix_kit_web/live/dashboard.html.heex:170 #: lib/phoenix_kit_web/live/dashboard.html.heex:175 #: lib/phoenix_kit_web/live/users/sessions.html.heex:18 @@ -426,7 +427,7 @@ msgstr "" msgid "%{language} (Published)" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:383 +#: lib/phoenix_kit_web/live/components/user_settings.ex:403 #, elixir-autogen, elixir-format msgid "%{provider} account disconnected successfully" msgstr "" @@ -446,7 +447,7 @@ msgstr "" msgid "123 Business Street" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:196 +#: lib/phoenix_kit_web/live/components/user_settings.ex:216 #, elixir-autogen, elixir-format msgid "A link to confirm your email change has been sent to the new address." msgstr "" @@ -667,12 +668,12 @@ msgstr "" msgid "Cannot delete your own account" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:417 +#: lib/phoenix_kit_web/live/components/user_settings.ex:437 #, elixir-autogen, elixir-format msgid "Cannot disconnect %{provider}. Please ensure you have at least one sign-in method available." msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:412 +#: lib/phoenix_kit_web/live/components/user_settings.ex:432 #, elixir-autogen, elixir-format msgid "Cannot disconnect %{provider}. This is your only sign-in method. Please set a password or connect another provider first." msgstr "" @@ -1078,7 +1079,7 @@ msgstr "" msgid "Failed to delete user" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:403 +#: lib/phoenix_kit_web/live/components/user_settings.ex:423 #, elixir-autogen, elixir-format msgid "Failed to disconnect provider. Please try again." msgstr "" @@ -1274,6 +1275,7 @@ msgid "Italic" msgstr "" #: lib/phoenix_kit_web/users/login.html.heex:58 +#: lib/phoenix_kit_web/users/qr_login.html.heex:34 #, elixir-autogen, elixir-format msgid "Keep me logged in" msgstr "" @@ -1531,7 +1533,7 @@ msgstr "" msgid "Password" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:247 +#: lib/phoenix_kit_web/live/components/user_settings.ex:267 #, elixir-autogen, elixir-format msgid "Password changed successfully." msgstr "" @@ -1613,7 +1615,7 @@ msgstr "" msgid "Profile" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:319 +#: lib/phoenix_kit_web/live/components/user_settings.ex:339 #, elixir-autogen, elixir-format msgid "Profile updated successfully" msgstr "" @@ -1629,7 +1631,7 @@ msgstr "" msgid "Protected core roles" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:393 +#: lib/phoenix_kit_web/live/components/user_settings.ex:413 #, elixir-autogen, elixir-format msgid "Provider not found" msgstr "" @@ -3944,7 +3946,7 @@ msgstr "" msgid "Authorize access to your %{provider} account. This will open a sign-in page where you choose which account to connect." msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:76 +#: lib/phoenix_kit_web/live/components/user_settings.ex:89 #, elixir-autogen, elixir-format msgid "Avatar updated successfully!" msgstr "" @@ -4379,7 +4381,7 @@ msgstr "" msgid "Failed to save" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:473 +#: lib/phoenix_kit_web/live/components/user_settings.ex:493 #, elixir-autogen, elixir-format msgid "Failed to save notification preferences." msgstr "" @@ -4399,7 +4401,7 @@ msgstr "" msgid "Failed to toggle maintenance mode" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:83 +#: lib/phoenix_kit_web/live/components/user_settings.ex:96 #, elixir-autogen, elixir-format msgid "Failed to update avatar" msgstr "" @@ -4660,7 +4662,7 @@ msgstr "" msgid "Maintenance schedule saved" msgstr "" -#: lib/phoenix_kit_web/live/dashboard/settings.ex:55 +#: lib/phoenix_kit_web/live/dashboard/settings.ex:58 #, elixir-autogen, elixir-format msgid "Manage your account settings and preferences" msgstr "" @@ -4824,12 +4826,12 @@ msgstr "" msgid "Not tested" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:466 +#: lib/phoenix_kit_web/live/components/user_settings.ex:486 #, elixir-autogen, elixir-format msgid "Notification preferences saved." msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1113 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1184 #: lib/phoenix_kit_web/live/modules.html.heex:667 #: lib/phoenix_kit_web/live/modules/notifications/index.html.heex:5 #: lib/phoenix_kit_web/live/settings.html.heex:318 @@ -4915,7 +4917,7 @@ msgstr "" msgid "Person" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1124 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1195 #, elixir-autogen, elixir-format msgid "Pick which notification types you want to receive. Unchecked types are muted — activities still record in the audit log but no bell notification is created for you." msgstr "" @@ -5006,7 +5008,7 @@ msgstr "" msgid "Save Tax Settings" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1161 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1232 #, elixir-autogen, elixir-format msgid "Save preferences" msgstr "" @@ -10140,12 +10142,12 @@ msgstr "" msgid "Approve this sign-in?" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:125 +#: lib/phoenix_kit_web/users/qr_login.ex:153 #, elixir-autogen, elixir-format msgid "Approved — signing you in…" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:37 +#: lib/phoenix_kit_web/users/qr_login.html.heex:49 #, elixir-autogen, elixir-format msgid "Back to sign in" msgstr "" @@ -10214,12 +10216,12 @@ msgstr "" msgid "Only approve if this is you. Approving signs that browser in to your account." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:27 +#: lib/phoenix_kit_web/users/qr_login.html.heex:39 #, elixir-autogen, elixir-format msgid "Open the camera on your signed-in phone." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:28 +#: lib/phoenix_kit_web/users/qr_login.html.heex:40 #, elixir-autogen, elixir-format msgid "Point it at the code above." msgstr "" @@ -10234,7 +10236,7 @@ msgstr "" msgid "QR Code" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:37 +#: lib/phoenix_kit_web/users/qr_login.ex:42 #: lib/phoenix_kit_web/users/qr_login_confirm.ex:32 #: lib/phoenix_kit_web/users/qr_login_confirm.ex:76 #, elixir-autogen, elixir-format @@ -10251,12 +10253,12 @@ msgstr "" msgid "Requested" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:122 +#: lib/phoenix_kit_web/users/qr_login.ex:150 #, elixir-autogen, elixir-format msgid "Scan this code with your phone's camera, then approve the sign-in on your phone." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:128 +#: lib/phoenix_kit_web/users/qr_login.ex:156 #, elixir-autogen, elixir-format msgid "Show a new code" msgstr "" @@ -10273,28 +10275,28 @@ msgstr "" msgid "Signed in" msgstr "" -#: lib/phoenix_kit_web/users/qr_login_complete.ex:26 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:29 #, elixir-autogen, elixir-format msgid "Signed in with QR code." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:29 +#: lib/phoenix_kit_web/users/qr_login.html.heex:41 #, elixir-autogen, elixir-format msgid "Tap Approve on your phone to finish signing in here." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:126 +#: lib/phoenix_kit_web/users/qr_login.ex:154 #, elixir-autogen, elixir-format msgid "The sign-in request was denied." msgstr "" -#: lib/phoenix_kit_web/users/qr_login_complete.ex:31 -#: lib/phoenix_kit_web/users/qr_login_complete.ex:38 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:34 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:41 #, elixir-autogen, elixir-format msgid "This QR sign-in link is invalid or has expired." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:127 +#: lib/phoenix_kit_web/users/qr_login.ex:155 #, elixir-autogen, elixir-format msgid "This code expired." msgstr "" @@ -10383,7 +10385,7 @@ msgstr "" msgid "Or add via" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:56 +#: lib/phoenix_kit_web/users/qr_login.ex:61 #, elixir-autogen, elixir-format msgid "Too many attempts. Please try again shortly." msgstr "" @@ -10497,3 +10499,78 @@ msgstr "" #, elixir-autogen, elixir-format msgid "xAI requires a funded account before the API will return completions" msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:547 +#, elixir-autogen, elixir-format +msgid "Active %{count} days ago" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:540 +#, elixir-autogen, elixir-format +msgid "Active now" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:545 +#, elixir-autogen, elixir-format +msgid "Active today" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:546 +#, elixir-autogen, elixir-format +msgid "Active yesterday" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1258 +#, elixir-autogen, elixir-format +msgid "Devices currently signed in to your account. If you don't recognize one, sign it out." +msgstr "" + +#: lib/phoenix_kit/users/login_alerts.ex:140 +#, elixir-autogen, elixir-format +msgid "New sign-in to your account from %{details}." +msgstr "" + +#: lib/phoenix_kit/users/login_alerts.ex:139 +#, elixir-autogen, elixir-format +msgid "New sign-in to your account." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1291 +#, elixir-autogen, elixir-format +msgid "Sign out" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1302 +#, elixir-autogen, elixir-format +msgid "Sign out of all other sessions?" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1305 +#, elixir-autogen, elixir-format +msgid "Sign out other sessions" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:520 +#, elixir-autogen, elixir-format +msgid "Signed out of all other sessions." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:503 +#, elixir-autogen, elixir-format +msgid "Signed out of that session." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:504 +#, elixir-autogen, elixir-format +msgid "That session is no longer active." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1275 +#, elixir-autogen, elixir-format +msgid "This device" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:532 +#, elixir-autogen, elixir-format +msgid "Unknown device" +msgstr "" diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po index 20e24be80..50b1e34db 100644 --- a/priv/gettext/en/LC_MESSAGES/default.po +++ b/priv/gettext/en/LC_MESSAGES/default.po @@ -168,6 +168,7 @@ msgstr "" msgid "Registered accounts" msgstr "" +#: lib/phoenix_kit_web/live/components/user_settings.ex:1247 #: lib/phoenix_kit_web/live/dashboard.html.heex:170 #: lib/phoenix_kit_web/live/dashboard.html.heex:175 #: lib/phoenix_kit_web/live/users/sessions.html.heex:18 @@ -427,7 +428,7 @@ msgstr "" msgid "%{language} (Published)" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:383 +#: lib/phoenix_kit_web/live/components/user_settings.ex:403 #, elixir-autogen, elixir-format msgid "%{provider} account disconnected successfully" msgstr "" @@ -447,7 +448,7 @@ msgstr "" msgid "123 Business Street" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:196 +#: lib/phoenix_kit_web/live/components/user_settings.ex:216 #, elixir-autogen, elixir-format msgid "A link to confirm your email change has been sent to the new address." msgstr "" @@ -668,12 +669,12 @@ msgstr "" msgid "Cannot delete your own account" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:417 +#: lib/phoenix_kit_web/live/components/user_settings.ex:437 #, elixir-autogen, elixir-format msgid "Cannot disconnect %{provider}. Please ensure you have at least one sign-in method available." msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:412 +#: lib/phoenix_kit_web/live/components/user_settings.ex:432 #, elixir-autogen, elixir-format msgid "Cannot disconnect %{provider}. This is your only sign-in method. Please set a password or connect another provider first." msgstr "" @@ -1079,7 +1080,7 @@ msgstr "" msgid "Failed to delete user" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:403 +#: lib/phoenix_kit_web/live/components/user_settings.ex:423 #, elixir-autogen, elixir-format msgid "Failed to disconnect provider. Please try again." msgstr "" @@ -1275,6 +1276,7 @@ msgid "Italic" msgstr "" #: lib/phoenix_kit_web/users/login.html.heex:58 +#: lib/phoenix_kit_web/users/qr_login.html.heex:34 #, elixir-autogen, elixir-format msgid "Keep me logged in" msgstr "" @@ -1532,7 +1534,7 @@ msgstr "" msgid "Password" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:247 +#: lib/phoenix_kit_web/live/components/user_settings.ex:267 #, elixir-autogen, elixir-format msgid "Password changed successfully." msgstr "" @@ -1614,7 +1616,7 @@ msgstr "" msgid "Profile" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:319 +#: lib/phoenix_kit_web/live/components/user_settings.ex:339 #, elixir-autogen, elixir-format msgid "Profile updated successfully" msgstr "" @@ -1630,7 +1632,7 @@ msgstr "" msgid "Protected core roles" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:393 +#: lib/phoenix_kit_web/live/components/user_settings.ex:413 #, elixir-autogen, elixir-format msgid "Provider not found" msgstr "" @@ -3945,7 +3947,7 @@ msgstr "" msgid "Authorize access to your %{provider} account. This will open a sign-in page where you choose which account to connect." msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:76 +#: lib/phoenix_kit_web/live/components/user_settings.ex:89 #, elixir-autogen, elixir-format, fuzzy msgid "Avatar updated successfully!" msgstr "" @@ -4380,7 +4382,7 @@ msgstr "" msgid "Failed to save" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:473 +#: lib/phoenix_kit_web/live/components/user_settings.ex:493 #, elixir-autogen, elixir-format msgid "Failed to save notification preferences." msgstr "" @@ -4400,7 +4402,7 @@ msgstr "" msgid "Failed to toggle maintenance mode" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:83 +#: lib/phoenix_kit_web/live/components/user_settings.ex:96 #, elixir-autogen, elixir-format, fuzzy msgid "Failed to update avatar" msgstr "" @@ -4661,7 +4663,7 @@ msgstr "" msgid "Maintenance schedule saved" msgstr "" -#: lib/phoenix_kit_web/live/dashboard/settings.ex:55 +#: lib/phoenix_kit_web/live/dashboard/settings.ex:58 #, elixir-autogen, elixir-format msgid "Manage your account settings and preferences" msgstr "" @@ -4825,12 +4827,12 @@ msgstr "" msgid "Not tested" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:466 +#: lib/phoenix_kit_web/live/components/user_settings.ex:486 #, elixir-autogen, elixir-format msgid "Notification preferences saved." msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1113 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1184 #: lib/phoenix_kit_web/live/modules.html.heex:667 #: lib/phoenix_kit_web/live/modules/notifications/index.html.heex:5 #: lib/phoenix_kit_web/live/settings.html.heex:318 @@ -4916,7 +4918,7 @@ msgstr "" msgid "Person" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1124 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1195 #, elixir-autogen, elixir-format msgid "Pick which notification types you want to receive. Unchecked types are muted — activities still record in the audit log but no bell notification is created for you." msgstr "" @@ -5007,7 +5009,7 @@ msgstr "" msgid "Save Tax Settings" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1161 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1232 #, elixir-autogen, elixir-format, fuzzy msgid "Save preferences" msgstr "" @@ -10141,12 +10143,12 @@ msgstr "" msgid "Approve this sign-in?" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:125 +#: lib/phoenix_kit_web/users/qr_login.ex:153 #, elixir-autogen, elixir-format msgid "Approved — signing you in…" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:37 +#: lib/phoenix_kit_web/users/qr_login.html.heex:49 #, elixir-autogen, elixir-format msgid "Back to sign in" msgstr "" @@ -10215,12 +10217,12 @@ msgstr "" msgid "Only approve if this is you. Approving signs that browser in to your account." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:27 +#: lib/phoenix_kit_web/users/qr_login.html.heex:39 #, elixir-autogen, elixir-format msgid "Open the camera on your signed-in phone." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:28 +#: lib/phoenix_kit_web/users/qr_login.html.heex:40 #, elixir-autogen, elixir-format msgid "Point it at the code above." msgstr "" @@ -10235,7 +10237,7 @@ msgstr "" msgid "QR Code" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:37 +#: lib/phoenix_kit_web/users/qr_login.ex:42 #: lib/phoenix_kit_web/users/qr_login_confirm.ex:32 #: lib/phoenix_kit_web/users/qr_login_confirm.ex:76 #, elixir-autogen, elixir-format @@ -10252,12 +10254,12 @@ msgstr "" msgid "Requested" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:122 +#: lib/phoenix_kit_web/users/qr_login.ex:150 #, elixir-autogen, elixir-format msgid "Scan this code with your phone's camera, then approve the sign-in on your phone." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:128 +#: lib/phoenix_kit_web/users/qr_login.ex:156 #, elixir-autogen, elixir-format msgid "Show a new code" msgstr "" @@ -10274,28 +10276,28 @@ msgstr "" msgid "Signed in" msgstr "" -#: lib/phoenix_kit_web/users/qr_login_complete.ex:26 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:29 #, elixir-autogen, elixir-format msgid "Signed in with QR code." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:29 +#: lib/phoenix_kit_web/users/qr_login.html.heex:41 #, elixir-autogen, elixir-format msgid "Tap Approve on your phone to finish signing in here." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:126 +#: lib/phoenix_kit_web/users/qr_login.ex:154 #, elixir-autogen, elixir-format msgid "The sign-in request was denied." msgstr "" -#: lib/phoenix_kit_web/users/qr_login_complete.ex:31 -#: lib/phoenix_kit_web/users/qr_login_complete.ex:38 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:34 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:41 #, elixir-autogen, elixir-format msgid "This QR sign-in link is invalid or has expired." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:127 +#: lib/phoenix_kit_web/users/qr_login.ex:155 #, elixir-autogen, elixir-format msgid "This code expired." msgstr "" @@ -10384,7 +10386,7 @@ msgstr "" msgid "Or add via" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:56 +#: lib/phoenix_kit_web/users/qr_login.ex:61 #, elixir-autogen, elixir-format msgid "Too many attempts. Please try again shortly." msgstr "" @@ -10498,3 +10500,78 @@ msgstr "" #, elixir-autogen, elixir-format msgid "xAI requires a funded account before the API will return completions" msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:547 +#, elixir-autogen, elixir-format +msgid "Active %{count} days ago" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:540 +#, elixir-autogen, elixir-format, fuzzy +msgid "Active now" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:545 +#, elixir-autogen, elixir-format, fuzzy +msgid "Active today" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:546 +#, elixir-autogen, elixir-format +msgid "Active yesterday" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1258 +#, elixir-autogen, elixir-format +msgid "Devices currently signed in to your account. If you don't recognize one, sign it out." +msgstr "" + +#: lib/phoenix_kit/users/login_alerts.ex:140 +#, elixir-autogen, elixir-format +msgid "New sign-in to your account from %{details}." +msgstr "" + +#: lib/phoenix_kit/users/login_alerts.ex:139 +#, elixir-autogen, elixir-format +msgid "New sign-in to your account." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1291 +#, elixir-autogen, elixir-format, fuzzy +msgid "Sign out" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1302 +#, elixir-autogen, elixir-format +msgid "Sign out of all other sessions?" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1305 +#, elixir-autogen, elixir-format +msgid "Sign out other sessions" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:520 +#, elixir-autogen, elixir-format +msgid "Signed out of all other sessions." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:503 +#, elixir-autogen, elixir-format +msgid "Signed out of that session." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:504 +#, elixir-autogen, elixir-format +msgid "That session is no longer active." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1275 +#, elixir-autogen, elixir-format +msgid "This device" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:532 +#, elixir-autogen, elixir-format, fuzzy +msgid "Unknown device" +msgstr "" diff --git a/priv/gettext/es/LC_MESSAGES/default.po b/priv/gettext/es/LC_MESSAGES/default.po index c5c0dfeed..b71afab03 100644 --- a/priv/gettext/es/LC_MESSAGES/default.po +++ b/priv/gettext/es/LC_MESSAGES/default.po @@ -170,6 +170,7 @@ msgstr "Total de Usuarios" msgid "Registered accounts" msgstr "Cuentas registradas" +#: lib/phoenix_kit_web/live/components/user_settings.ex:1247 #: lib/phoenix_kit_web/live/dashboard.html.heex:170 #: lib/phoenix_kit_web/live/dashboard.html.heex:175 #: lib/phoenix_kit_web/live/users/sessions.html.heex:18 @@ -430,7 +431,7 @@ msgstr "" msgid "%{language} (Published)" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:383 +#: lib/phoenix_kit_web/live/components/user_settings.ex:403 #, elixir-autogen, elixir-format msgid "%{provider} account disconnected successfully" msgstr "" @@ -450,7 +451,7 @@ msgstr "" msgid "123 Business Street" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:196 +#: lib/phoenix_kit_web/live/components/user_settings.ex:216 #, elixir-autogen, elixir-format msgid "A link to confirm your email change has been sent to the new address." msgstr "" @@ -671,12 +672,12 @@ msgstr "" msgid "Cannot delete your own account" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:417 +#: lib/phoenix_kit_web/live/components/user_settings.ex:437 #, elixir-autogen, elixir-format msgid "Cannot disconnect %{provider}. Please ensure you have at least one sign-in method available." msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:412 +#: lib/phoenix_kit_web/live/components/user_settings.ex:432 #, elixir-autogen, elixir-format msgid "Cannot disconnect %{provider}. This is your only sign-in method. Please set a password or connect another provider first." msgstr "" @@ -1082,7 +1083,7 @@ msgstr "" msgid "Failed to delete user" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:403 +#: lib/phoenix_kit_web/live/components/user_settings.ex:423 #, elixir-autogen, elixir-format msgid "Failed to disconnect provider. Please try again." msgstr "" @@ -1278,6 +1279,7 @@ msgid "Italic" msgstr "" #: lib/phoenix_kit_web/users/login.html.heex:58 +#: lib/phoenix_kit_web/users/qr_login.html.heex:34 #, elixir-autogen, elixir-format msgid "Keep me logged in" msgstr "" @@ -1535,7 +1537,7 @@ msgstr "Página publicada correctamente" msgid "Password" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:247 +#: lib/phoenix_kit_web/live/components/user_settings.ex:267 #, elixir-autogen, elixir-format msgid "Password changed successfully." msgstr "" @@ -1617,7 +1619,7 @@ msgstr "Preferencias de privacidad" msgid "Profile" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:319 +#: lib/phoenix_kit_web/live/components/user_settings.ex:339 #, elixir-autogen, elixir-format msgid "Profile updated successfully" msgstr "" @@ -1633,7 +1635,7 @@ msgstr "" msgid "Protected core roles" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:393 +#: lib/phoenix_kit_web/live/components/user_settings.ex:413 #, elixir-autogen, elixir-format msgid "Provider not found" msgstr "" @@ -3952,7 +3954,7 @@ msgstr "" msgid "Authorize access to your %{provider} account. This will open a sign-in page where you choose which account to connect." msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:76 +#: lib/phoenix_kit_web/live/components/user_settings.ex:89 #, elixir-autogen, elixir-format, fuzzy msgid "Avatar updated successfully!" msgstr "" @@ -4387,7 +4389,7 @@ msgstr "" msgid "Failed to save" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:473 +#: lib/phoenix_kit_web/live/components/user_settings.ex:493 #, elixir-autogen, elixir-format msgid "Failed to save notification preferences." msgstr "" @@ -4407,7 +4409,7 @@ msgstr "No se pudo guardar la configuración" msgid "Failed to toggle maintenance mode" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:83 +#: lib/phoenix_kit_web/live/components/user_settings.ex:96 #, elixir-autogen, elixir-format, fuzzy msgid "Failed to update avatar" msgstr "" @@ -4668,7 +4670,7 @@ msgstr "" msgid "Maintenance schedule saved" msgstr "" -#: lib/phoenix_kit_web/live/dashboard/settings.ex:55 +#: lib/phoenix_kit_web/live/dashboard/settings.ex:58 #, elixir-autogen, elixir-format msgid "Manage your account settings and preferences" msgstr "" @@ -4832,12 +4834,12 @@ msgstr "" msgid "Not tested" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:466 +#: lib/phoenix_kit_web/live/components/user_settings.ex:486 #, elixir-autogen, elixir-format msgid "Notification preferences saved." msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1113 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1184 #: lib/phoenix_kit_web/live/modules.html.heex:667 #: lib/phoenix_kit_web/live/modules/notifications/index.html.heex:5 #: lib/phoenix_kit_web/live/settings.html.heex:318 @@ -4923,7 +4925,7 @@ msgstr "" msgid "Person" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1124 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1195 #, elixir-autogen, elixir-format msgid "Pick which notification types you want to receive. Unchecked types are muted — activities still record in the audit log but no bell notification is created for you." msgstr "" @@ -5014,7 +5016,7 @@ msgstr "" msgid "Save Tax Settings" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1161 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1232 #, elixir-autogen, elixir-format, fuzzy msgid "Save preferences" msgstr "Guardar preferencias" @@ -10149,12 +10151,12 @@ msgstr "" msgid "Approve this sign-in?" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:125 +#: lib/phoenix_kit_web/users/qr_login.ex:153 #, elixir-autogen, elixir-format msgid "Approved — signing you in…" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:37 +#: lib/phoenix_kit_web/users/qr_login.html.heex:49 #, elixir-autogen, elixir-format msgid "Back to sign in" msgstr "" @@ -10223,12 +10225,12 @@ msgstr "" msgid "Only approve if this is you. Approving signs that browser in to your account." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:27 +#: lib/phoenix_kit_web/users/qr_login.html.heex:39 #, elixir-autogen, elixir-format msgid "Open the camera on your signed-in phone." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:28 +#: lib/phoenix_kit_web/users/qr_login.html.heex:40 #, elixir-autogen, elixir-format msgid "Point it at the code above." msgstr "" @@ -10243,7 +10245,7 @@ msgstr "" msgid "QR Code" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:37 +#: lib/phoenix_kit_web/users/qr_login.ex:42 #: lib/phoenix_kit_web/users/qr_login_confirm.ex:32 #: lib/phoenix_kit_web/users/qr_login_confirm.ex:76 #, elixir-autogen, elixir-format @@ -10260,12 +10262,12 @@ msgstr "" msgid "Requested" msgstr "Obligatorio" -#: lib/phoenix_kit_web/users/qr_login.ex:122 +#: lib/phoenix_kit_web/users/qr_login.ex:150 #, elixir-autogen, elixir-format msgid "Scan this code with your phone's camera, then approve the sign-in on your phone." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:128 +#: lib/phoenix_kit_web/users/qr_login.ex:156 #, elixir-autogen, elixir-format msgid "Show a new code" msgstr "" @@ -10282,28 +10284,28 @@ msgstr "" msgid "Signed in" msgstr "" -#: lib/phoenix_kit_web/users/qr_login_complete.ex:26 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:29 #, elixir-autogen, elixir-format msgid "Signed in with QR code." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:29 +#: lib/phoenix_kit_web/users/qr_login.html.heex:41 #, elixir-autogen, elixir-format msgid "Tap Approve on your phone to finish signing in here." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:126 +#: lib/phoenix_kit_web/users/qr_login.ex:154 #, elixir-autogen, elixir-format msgid "The sign-in request was denied." msgstr "" -#: lib/phoenix_kit_web/users/qr_login_complete.ex:31 -#: lib/phoenix_kit_web/users/qr_login_complete.ex:38 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:34 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:41 #, elixir-autogen, elixir-format msgid "This QR sign-in link is invalid or has expired." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:127 +#: lib/phoenix_kit_web/users/qr_login.ex:155 #, elixir-autogen, elixir-format msgid "This code expired." msgstr "" @@ -10392,7 +10394,7 @@ msgstr "" msgid "Or add via" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:56 +#: lib/phoenix_kit_web/users/qr_login.ex:61 #, elixir-autogen, elixir-format msgid "Too many attempts. Please try again shortly." msgstr "" @@ -10506,3 +10508,78 @@ msgstr "" #, elixir-autogen, elixir-format msgid "xAI requires a funded account before the API will return completions" msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:547 +#, elixir-autogen, elixir-format +msgid "Active %{count} days ago" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:540 +#, elixir-autogen, elixir-format, fuzzy +msgid "Active now" +msgstr "Activo" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:545 +#, elixir-autogen, elixir-format, fuzzy +msgid "Active today" +msgstr "Activo" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:546 +#, elixir-autogen, elixir-format +msgid "Active yesterday" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1258 +#, elixir-autogen, elixir-format +msgid "Devices currently signed in to your account. If you don't recognize one, sign it out." +msgstr "" + +#: lib/phoenix_kit/users/login_alerts.ex:140 +#, elixir-autogen, elixir-format +msgid "New sign-in to your account from %{details}." +msgstr "" + +#: lib/phoenix_kit/users/login_alerts.ex:139 +#, elixir-autogen, elixir-format +msgid "New sign-in to your account." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1291 +#, elixir-autogen, elixir-format, fuzzy +msgid "Sign out" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1302 +#, elixir-autogen, elixir-format +msgid "Sign out of all other sessions?" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1305 +#, elixir-autogen, elixir-format +msgid "Sign out other sessions" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:520 +#, elixir-autogen, elixir-format +msgid "Signed out of all other sessions." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:503 +#, elixir-autogen, elixir-format +msgid "Signed out of that session." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:504 +#, elixir-autogen, elixir-format +msgid "That session is no longer active." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1275 +#, elixir-autogen, elixir-format +msgid "This device" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:532 +#, elixir-autogen, elixir-format, fuzzy +msgid "Unknown device" +msgstr "" diff --git a/priv/gettext/et/LC_MESSAGES/default.po b/priv/gettext/et/LC_MESSAGES/default.po index 9e01c2dbb..c5a42e057 100644 --- a/priv/gettext/et/LC_MESSAGES/default.po +++ b/priv/gettext/et/LC_MESSAGES/default.po @@ -178,6 +178,7 @@ msgstr "Kasutajaid kokku" msgid "Registered accounts" msgstr "Registreeritud kontod" +#: lib/phoenix_kit_web/live/components/user_settings.ex:1247 #: lib/phoenix_kit_web/live/dashboard.html.heex:170 #: lib/phoenix_kit_web/live/dashboard.html.heex:175 #: lib/phoenix_kit_web/live/users/sessions.html.heex:18 @@ -438,7 +439,7 @@ msgstr "%{language} (Mustand)" msgid "%{language} (Published)" msgstr "%{language} (Avaldatud)" -#: lib/phoenix_kit_web/live/components/user_settings.ex:383 +#: lib/phoenix_kit_web/live/components/user_settings.ex:403 #, elixir-autogen, elixir-format msgid "%{provider} account disconnected successfully" msgstr "%{provider} konto lahti ühendatud edukalt" @@ -458,7 +459,7 @@ msgstr "(valikuline)" msgid "123 Business Street" msgstr "123 Äritänav" -#: lib/phoenix_kit_web/live/components/user_settings.ex:196 +#: lib/phoenix_kit_web/live/components/user_settings.ex:216 #, elixir-autogen, elixir-format msgid "A link to confirm your email change has been sent to the new address." msgstr "E-posti muutmise kinnituslink on saadetud uuele aadressile." @@ -679,12 +680,12 @@ msgstr "Viimast süsteemi omanikku ei saa kustutada" msgid "Cannot delete your own account" msgstr "Oma kontot ei saa kustutada" -#: lib/phoenix_kit_web/live/components/user_settings.ex:417 +#: lib/phoenix_kit_web/live/components/user_settings.ex:437 #, elixir-autogen, elixir-format msgid "Cannot disconnect %{provider}. Please ensure you have at least one sign-in method available." msgstr "Ei saa %{provider} lahti ühendada. Palun veendu, et sul on vähemalt üks sisselogimismeetod saadaval." -#: lib/phoenix_kit_web/live/components/user_settings.ex:412 +#: lib/phoenix_kit_web/live/components/user_settings.ex:432 #, elixir-autogen, elixir-format msgid "Cannot disconnect %{provider}. This is your only sign-in method. Please set a password or connect another provider first." msgstr "Ei saa %{provider} lahti ühendada. See on sinu ainus sisselogimismeetod. Palun määra parool või ühenda kõigepealt teine teenusepakkuja." @@ -1090,7 +1091,7 @@ msgstr "Rolli kustutamine ebaõnnestus" msgid "Failed to delete user" msgstr "Kasutaja kustutamine ebaõnnestus" -#: lib/phoenix_kit_web/live/components/user_settings.ex:403 +#: lib/phoenix_kit_web/live/components/user_settings.ex:423 #, elixir-autogen, elixir-format msgid "Failed to disconnect provider. Please try again." msgstr "Teenusepakkuja lahtiühendamine ebaõnnestus. Palun proovi uuesti." @@ -1286,6 +1287,7 @@ msgid "Italic" msgstr "Kaldkiri" #: lib/phoenix_kit_web/users/login.html.heex:58 +#: lib/phoenix_kit_web/users/qr_login.html.heex:34 #, elixir-autogen, elixir-format msgid "Keep me logged in" msgstr "Jäta mind sisselogituks" @@ -1543,7 +1545,7 @@ msgstr "Leht avaldatud edukalt" msgid "Password" msgstr "Parool" -#: lib/phoenix_kit_web/live/components/user_settings.ex:247 +#: lib/phoenix_kit_web/live/components/user_settings.ex:267 #, elixir-autogen, elixir-format msgid "Password changed successfully." msgstr "Parool muudetud edukalt." @@ -1625,7 +1627,7 @@ msgstr "Privaatsuse eelistused" msgid "Profile" msgstr "Profiil" -#: lib/phoenix_kit_web/live/components/user_settings.ex:319 +#: lib/phoenix_kit_web/live/components/user_settings.ex:339 #, elixir-autogen, elixir-format msgid "Profile updated successfully" msgstr "Profiil uuendatud edukalt" @@ -1641,7 +1643,7 @@ msgstr "Kaitstud" msgid "Protected core roles" msgstr "Kaitstud põhirollid" -#: lib/phoenix_kit_web/live/components/user_settings.ex:393 +#: lib/phoenix_kit_web/live/components/user_settings.ex:413 #, elixir-autogen, elixir-format msgid "Provider not found" msgstr "Teenusepakkujat ei leitud" @@ -3958,7 +3960,7 @@ msgstr "Volitamine ebaõnnestus: %{reason}" msgid "Authorize access to your %{provider} account. This will open a sign-in page where you choose which account to connect." msgstr "Volita juurdepääs oma %{provider} kontole. See avab sisselogimislehe, kus saad valida, millise konto ühendada." -#: lib/phoenix_kit_web/live/components/user_settings.ex:76 +#: lib/phoenix_kit_web/live/components/user_settings.ex:89 #, elixir-autogen, elixir-format, fuzzy msgid "Avatar updated successfully!" msgstr "Avatar uuendatud edukalt!" @@ -4393,7 +4395,7 @@ msgstr "Liikme eemaldamine ebaõnnestus" msgid "Failed to save" msgstr "Salvestamine ebaõnnestus" -#: lib/phoenix_kit_web/live/components/user_settings.ex:473 +#: lib/phoenix_kit_web/live/components/user_settings.ex:493 #, elixir-autogen, elixir-format msgid "Failed to save notification preferences." msgstr "Teatiste eelistuste salvestamine ebaõnnestus." @@ -4413,7 +4415,7 @@ msgstr "Kutse saatmine ebaõnnestus" msgid "Failed to toggle maintenance mode" msgstr "Hoolduseolekurežiimi lülitamine ebaõnnestus" -#: lib/phoenix_kit_web/live/components/user_settings.ex:83 +#: lib/phoenix_kit_web/live/components/user_settings.ex:96 #, elixir-autogen, elixir-format, fuzzy msgid "Failed to update avatar" msgstr "Avatari uuendamine ebaõnnestus" @@ -4674,7 +4676,7 @@ msgstr "Hoolduse ajakava tühistatud" msgid "Maintenance schedule saved" msgstr "Hoolduse ajakava salvestatud" -#: lib/phoenix_kit_web/live/dashboard/settings.ex:55 +#: lib/phoenix_kit_web/live/dashboard/settings.ex:58 #, elixir-autogen, elixir-format msgid "Manage your account settings and preferences" msgstr "Halda oma konto seadeid ja eelistusi" @@ -4838,12 +4840,12 @@ msgstr "Ühendamata" msgid "Not tested" msgstr "Testimata" -#: lib/phoenix_kit_web/live/components/user_settings.ex:466 +#: lib/phoenix_kit_web/live/components/user_settings.ex:486 #, elixir-autogen, elixir-format msgid "Notification preferences saved." msgstr "Teatiste eelistused salvestatud." -#: lib/phoenix_kit_web/live/components/user_settings.ex:1113 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1184 #: lib/phoenix_kit_web/live/modules.html.heex:667 #: lib/phoenix_kit_web/live/modules/notifications/index.html.heex:5 #: lib/phoenix_kit_web/live/settings.html.heex:318 @@ -4929,7 +4931,7 @@ msgstr "Kasutajapõhine postkast, mida juhib tegevusvoog" msgid "Person" msgstr "Isik" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1124 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1195 #, elixir-autogen, elixir-format msgid "Pick which notification types you want to receive. Unchecked types are muted — activities still record in the audit log but no bell notification is created for you." msgstr "Vali, milliseid teatiste tüüpe soovid saada. Märkimata tüübid on summutatud — tegevused salvestatakse ikkagi auditilogi, kuid kellukese teatist ei looda." @@ -5020,7 +5022,7 @@ msgstr "Salvesta ajakava" msgid "Save Tax Settings" msgstr "Salvesta maksuseaded" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1161 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1232 #, elixir-autogen, elixir-format, fuzzy msgid "Save preferences" msgstr "Salvesta eelistused" @@ -10156,12 +10158,12 @@ msgstr "Apple sisselogimine" msgid "Approve this sign-in?" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:125 +#: lib/phoenix_kit_web/users/qr_login.ex:153 #, elixir-autogen, elixir-format msgid "Approved — signing you in…" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:37 +#: lib/phoenix_kit_web/users/qr_login.html.heex:49 #, elixir-autogen, elixir-format msgid "Back to sign in" msgstr "" @@ -10230,12 +10232,12 @@ msgstr "" msgid "Only approve if this is you. Approving signs that browser in to your account." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:27 +#: lib/phoenix_kit_web/users/qr_login.html.heex:39 #, elixir-autogen, elixir-format msgid "Open the camera on your signed-in phone." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:28 +#: lib/phoenix_kit_web/users/qr_login.html.heex:40 #, elixir-autogen, elixir-format msgid "Point it at the code above." msgstr "" @@ -10250,7 +10252,7 @@ msgstr "" msgid "QR Code" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:37 +#: lib/phoenix_kit_web/users/qr_login.ex:42 #: lib/phoenix_kit_web/users/qr_login_confirm.ex:32 #: lib/phoenix_kit_web/users/qr_login_confirm.ex:76 #, elixir-autogen, elixir-format @@ -10267,12 +10269,12 @@ msgstr "" msgid "Requested" msgstr "Kohustuslik" -#: lib/phoenix_kit_web/users/qr_login.ex:122 +#: lib/phoenix_kit_web/users/qr_login.ex:150 #, elixir-autogen, elixir-format msgid "Scan this code with your phone's camera, then approve the sign-in on your phone." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:128 +#: lib/phoenix_kit_web/users/qr_login.ex:156 #, elixir-autogen, elixir-format msgid "Show a new code" msgstr "" @@ -10290,28 +10292,28 @@ msgstr "Logi sisse parooliga" msgid "Signed in" msgstr "Logi sisse" -#: lib/phoenix_kit_web/users/qr_login_complete.ex:26 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:29 #, elixir-autogen, elixir-format msgid "Signed in with QR code." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:29 +#: lib/phoenix_kit_web/users/qr_login.html.heex:41 #, elixir-autogen, elixir-format msgid "Tap Approve on your phone to finish signing in here." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:126 +#: lib/phoenix_kit_web/users/qr_login.ex:154 #, elixir-autogen, elixir-format msgid "The sign-in request was denied." msgstr "" -#: lib/phoenix_kit_web/users/qr_login_complete.ex:31 -#: lib/phoenix_kit_web/users/qr_login_complete.ex:38 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:34 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:41 #, elixir-autogen, elixir-format msgid "This QR sign-in link is invalid or has expired." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:127 +#: lib/phoenix_kit_web/users/qr_login.ex:155 #, elixir-autogen, elixir-format msgid "This code expired." msgstr "" @@ -10400,7 +10402,7 @@ msgstr "" msgid "Or add via" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:56 +#: lib/phoenix_kit_web/users/qr_login.ex:61 #, elixir-autogen, elixir-format msgid "Too many attempts. Please try again shortly." msgstr "" @@ -10514,3 +10516,79 @@ msgstr "" #, elixir-autogen, elixir-format msgid "xAI requires a funded account before the API will return completions" msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:547 +#, elixir-autogen, elixir-format +msgid "Active %{count} days ago" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:540 +#, elixir-autogen, elixir-format, fuzzy +msgid "Active now" +msgstr "Aktiivne" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:545 +#, elixir-autogen, elixir-format, fuzzy +msgid "Active today" +msgstr "Aktiveeri" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:546 +#, elixir-autogen, elixir-format +msgid "Active yesterday" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1258 +#, elixir-autogen, elixir-format +msgid "Devices currently signed in to your account. If you don't recognize one, sign it out." +msgstr "" + +#: lib/phoenix_kit/users/login_alerts.ex:140 +#, elixir-autogen, elixir-format +msgid "New sign-in to your account from %{details}." +msgstr "" + +#: lib/phoenix_kit/users/login_alerts.ex:139 +#, elixir-autogen, elixir-format +msgid "New sign-in to your account." +msgstr "" + +## Login page translations +#: lib/phoenix_kit_web/live/components/user_settings.ex:1291 +#, elixir-autogen, elixir-format, fuzzy +msgid "Sign out" +msgstr "Logi sisse" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1302 +#, elixir-autogen, elixir-format +msgid "Sign out of all other sessions?" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1305 +#, elixir-autogen, elixir-format +msgid "Sign out other sessions" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:520 +#, elixir-autogen, elixir-format +msgid "Signed out of all other sessions." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:503 +#, elixir-autogen, elixir-format +msgid "Signed out of that session." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:504 +#, elixir-autogen, elixir-format +msgid "That session is no longer active." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1275 +#, elixir-autogen, elixir-format +msgid "This device" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:532 +#, elixir-autogen, elixir-format, fuzzy +msgid "Unknown device" +msgstr "Tundmatu" diff --git a/priv/gettext/fr/LC_MESSAGES/default.po b/priv/gettext/fr/LC_MESSAGES/default.po index ff67da004..0e9040d1a 100644 --- a/priv/gettext/fr/LC_MESSAGES/default.po +++ b/priv/gettext/fr/LC_MESSAGES/default.po @@ -168,6 +168,7 @@ msgstr "Total des utilisateurs" msgid "Registered accounts" msgstr "Comptes enregistrés" +#: lib/phoenix_kit_web/live/components/user_settings.ex:1247 #: lib/phoenix_kit_web/live/dashboard.html.heex:170 #: lib/phoenix_kit_web/live/dashboard.html.heex:175 #: lib/phoenix_kit_web/live/users/sessions.html.heex:18 @@ -427,7 +428,7 @@ msgstr "%{language} (Brouillon)" msgid "%{language} (Published)" msgstr "%{language} (Publié)" -#: lib/phoenix_kit_web/live/components/user_settings.ex:383 +#: lib/phoenix_kit_web/live/components/user_settings.ex:403 #, elixir-autogen, elixir-format msgid "%{provider} account disconnected successfully" msgstr "Compte %{provider} déconnecté avec succès" @@ -447,7 +448,7 @@ msgstr "(facultatif)" msgid "123 Business Street" msgstr "123 rue du Commerce" -#: lib/phoenix_kit_web/live/components/user_settings.ex:196 +#: lib/phoenix_kit_web/live/components/user_settings.ex:216 #, elixir-autogen, elixir-format msgid "A link to confirm your email change has been sent to the new address." msgstr "Un lien pour confirmer le changement de votre e-mail a été envoyé à la nouvelle adresse." @@ -668,12 +669,12 @@ msgstr "Impossible de supprimer le dernier propriétaire du système" msgid "Cannot delete your own account" msgstr "Impossible de supprimer votre propre compte" -#: lib/phoenix_kit_web/live/components/user_settings.ex:417 +#: lib/phoenix_kit_web/live/components/user_settings.ex:437 #, elixir-autogen, elixir-format msgid "Cannot disconnect %{provider}. Please ensure you have at least one sign-in method available." msgstr "Impossible de déconnecter %{provider}. Veuillez vous assurer de disposer d'au moins une méthode de connexion disponible." -#: lib/phoenix_kit_web/live/components/user_settings.ex:412 +#: lib/phoenix_kit_web/live/components/user_settings.ex:432 #, elixir-autogen, elixir-format msgid "Cannot disconnect %{provider}. This is your only sign-in method. Please set a password or connect another provider first." msgstr "Impossible de déconnecter %{provider}. Il s'agit de votre seule méthode de connexion. Veuillez d'abord définir un mot de passe ou connecter un autre fournisseur." @@ -1079,7 +1080,7 @@ msgstr "Échec de la suppression du rôle" msgid "Failed to delete user" msgstr "Échec de la suppression de l'utilisateur" -#: lib/phoenix_kit_web/live/components/user_settings.ex:403 +#: lib/phoenix_kit_web/live/components/user_settings.ex:423 #, elixir-autogen, elixir-format msgid "Failed to disconnect provider. Please try again." msgstr "Échec de la déconnexion du fournisseur. Veuillez réessayer." @@ -1275,6 +1276,7 @@ msgid "Italic" msgstr "Italique" #: lib/phoenix_kit_web/users/login.html.heex:58 +#: lib/phoenix_kit_web/users/qr_login.html.heex:34 #, elixir-autogen, elixir-format msgid "Keep me logged in" msgstr "Rester connecté" @@ -1532,7 +1534,7 @@ msgstr "Page publiée avec succès" msgid "Password" msgstr "Mot de passe" -#: lib/phoenix_kit_web/live/components/user_settings.ex:247 +#: lib/phoenix_kit_web/live/components/user_settings.ex:267 #, elixir-autogen, elixir-format msgid "Password changed successfully." msgstr "Mot de passe modifié avec succès." @@ -1614,7 +1616,7 @@ msgstr "Préférences de confidentialité" msgid "Profile" msgstr "Profil" -#: lib/phoenix_kit_web/live/components/user_settings.ex:319 +#: lib/phoenix_kit_web/live/components/user_settings.ex:339 #, elixir-autogen, elixir-format msgid "Profile updated successfully" msgstr "Profil mis à jour avec succès" @@ -1630,7 +1632,7 @@ msgstr "Protégé" msgid "Protected core roles" msgstr "Rôles principaux protégés" -#: lib/phoenix_kit_web/live/components/user_settings.ex:393 +#: lib/phoenix_kit_web/live/components/user_settings.ex:413 #, elixir-autogen, elixir-format msgid "Provider not found" msgstr "Fournisseur introuvable" @@ -3945,7 +3947,7 @@ msgstr "Échec de l'autorisation : %{reason}" msgid "Authorize access to your %{provider} account. This will open a sign-in page where you choose which account to connect." msgstr "Autorisez l'accès à votre compte %{provider}. Cela ouvrira une page de connexion où vous pourrez choisir le compte à connecter." -#: lib/phoenix_kit_web/live/components/user_settings.ex:76 +#: lib/phoenix_kit_web/live/components/user_settings.ex:89 #, elixir-autogen, elixir-format msgid "Avatar updated successfully!" msgstr "Avatar mis à jour avec succès !" @@ -4380,7 +4382,7 @@ msgstr "Échec de la suppression du membre" msgid "Failed to save" msgstr "Échec de l'enregistrement" -#: lib/phoenix_kit_web/live/components/user_settings.ex:473 +#: lib/phoenix_kit_web/live/components/user_settings.ex:493 #, elixir-autogen, elixir-format msgid "Failed to save notification preferences." msgstr "Échec de l'enregistrement des préférences de notification." @@ -4400,7 +4402,7 @@ msgstr "Impossible d'enregistrer les paramètres" msgid "Failed to toggle maintenance mode" msgstr "Échec du basculement du mode maintenance" -#: lib/phoenix_kit_web/live/components/user_settings.ex:83 +#: lib/phoenix_kit_web/live/components/user_settings.ex:96 #, elixir-autogen, elixir-format msgid "Failed to update avatar" msgstr "Échec de la mise à jour de l'avatar" @@ -4661,7 +4663,7 @@ msgstr "Planification de maintenance effacée" msgid "Maintenance schedule saved" msgstr "Planification de maintenance enregistrée" -#: lib/phoenix_kit_web/live/dashboard/settings.ex:55 +#: lib/phoenix_kit_web/live/dashboard/settings.ex:58 #, elixir-autogen, elixir-format msgid "Manage your account settings and preferences" msgstr "Gérez les paramètres et préférences de votre compte" @@ -4825,12 +4827,12 @@ msgstr "Non connecté" msgid "Not tested" msgstr "Non testé" -#: lib/phoenix_kit_web/live/components/user_settings.ex:466 +#: lib/phoenix_kit_web/live/components/user_settings.ex:486 #, elixir-autogen, elixir-format msgid "Notification preferences saved." msgstr "Préférences de notification enregistrées." -#: lib/phoenix_kit_web/live/components/user_settings.ex:1113 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1184 #: lib/phoenix_kit_web/live/modules.html.heex:667 #: lib/phoenix_kit_web/live/modules/notifications/index.html.heex:5 #: lib/phoenix_kit_web/live/settings.html.heex:318 @@ -4916,7 +4918,7 @@ msgstr "Boîte de réception par utilisateur alimentée par le flux d'activité" msgid "Person" msgstr "Personne" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1124 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1195 #, elixir-autogen, elixir-format msgid "Pick which notification types you want to receive. Unchecked types are muted — activities still record in the audit log but no bell notification is created for you." msgstr "Choisissez les types de notification que vous souhaitez recevoir. Les types non cochés sont désactivés — les activités sont toujours enregistrées dans le journal d'audit, mais aucune notification cloche n'est créée pour vous." @@ -5007,7 +5009,7 @@ msgstr "Enregistrer la planification" msgid "Save Tax Settings" msgstr "Enregistrer les paramètres de taxe" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1161 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1232 #, elixir-autogen, elixir-format, fuzzy msgid "Save preferences" msgstr "Enregistrer les préférences" @@ -10141,12 +10143,12 @@ msgstr "Connexion avec Apple" msgid "Approve this sign-in?" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:125 +#: lib/phoenix_kit_web/users/qr_login.ex:153 #, elixir-autogen, elixir-format msgid "Approved — signing you in…" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:37 +#: lib/phoenix_kit_web/users/qr_login.html.heex:49 #, elixir-autogen, elixir-format msgid "Back to sign in" msgstr "" @@ -10215,12 +10217,12 @@ msgstr "" msgid "Only approve if this is you. Approving signs that browser in to your account." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:27 +#: lib/phoenix_kit_web/users/qr_login.html.heex:39 #, elixir-autogen, elixir-format msgid "Open the camera on your signed-in phone." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:28 +#: lib/phoenix_kit_web/users/qr_login.html.heex:40 #, elixir-autogen, elixir-format msgid "Point it at the code above." msgstr "" @@ -10235,7 +10237,7 @@ msgstr "" msgid "QR Code" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:37 +#: lib/phoenix_kit_web/users/qr_login.ex:42 #: lib/phoenix_kit_web/users/qr_login_confirm.ex:32 #: lib/phoenix_kit_web/users/qr_login_confirm.ex:76 #, elixir-autogen, elixir-format @@ -10252,12 +10254,12 @@ msgstr "" msgid "Requested" msgstr "Obligatoire" -#: lib/phoenix_kit_web/users/qr_login.ex:122 +#: lib/phoenix_kit_web/users/qr_login.ex:150 #, elixir-autogen, elixir-format msgid "Scan this code with your phone's camera, then approve the sign-in on your phone." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:128 +#: lib/phoenix_kit_web/users/qr_login.ex:156 #, elixir-autogen, elixir-format msgid "Show a new code" msgstr "" @@ -10274,28 +10276,28 @@ msgstr "Se connecter avec un mot de passe" msgid "Signed in" msgstr "Se connecter" -#: lib/phoenix_kit_web/users/qr_login_complete.ex:26 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:29 #, elixir-autogen, elixir-format msgid "Signed in with QR code." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:29 +#: lib/phoenix_kit_web/users/qr_login.html.heex:41 #, elixir-autogen, elixir-format msgid "Tap Approve on your phone to finish signing in here." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:126 +#: lib/phoenix_kit_web/users/qr_login.ex:154 #, elixir-autogen, elixir-format msgid "The sign-in request was denied." msgstr "" -#: lib/phoenix_kit_web/users/qr_login_complete.ex:31 -#: lib/phoenix_kit_web/users/qr_login_complete.ex:38 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:34 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:41 #, elixir-autogen, elixir-format msgid "This QR sign-in link is invalid or has expired." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:127 +#: lib/phoenix_kit_web/users/qr_login.ex:155 #, elixir-autogen, elixir-format msgid "This code expired." msgstr "" @@ -10384,7 +10386,7 @@ msgstr "" msgid "Or add via" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:56 +#: lib/phoenix_kit_web/users/qr_login.ex:61 #, elixir-autogen, elixir-format msgid "Too many attempts. Please try again shortly." msgstr "" @@ -10498,3 +10500,78 @@ msgstr "" #, elixir-autogen, elixir-format msgid "xAI requires a funded account before the API will return completions" msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:547 +#, elixir-autogen, elixir-format +msgid "Active %{count} days ago" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:540 +#, elixir-autogen, elixir-format, fuzzy +msgid "Active now" +msgstr "Actif" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:545 +#, elixir-autogen, elixir-format, fuzzy +msgid "Active today" +msgstr "Activer" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:546 +#, elixir-autogen, elixir-format +msgid "Active yesterday" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1258 +#, elixir-autogen, elixir-format +msgid "Devices currently signed in to your account. If you don't recognize one, sign it out." +msgstr "" + +#: lib/phoenix_kit/users/login_alerts.ex:140 +#, elixir-autogen, elixir-format +msgid "New sign-in to your account from %{details}." +msgstr "" + +#: lib/phoenix_kit/users/login_alerts.ex:139 +#, elixir-autogen, elixir-format +msgid "New sign-in to your account." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1291 +#, elixir-autogen, elixir-format, fuzzy +msgid "Sign out" +msgstr "S'inscrire" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1302 +#, elixir-autogen, elixir-format +msgid "Sign out of all other sessions?" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1305 +#, elixir-autogen, elixir-format +msgid "Sign out other sessions" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:520 +#, elixir-autogen, elixir-format +msgid "Signed out of all other sessions." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:503 +#, elixir-autogen, elixir-format +msgid "Signed out of that session." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:504 +#, elixir-autogen, elixir-format +msgid "That session is no longer active." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1275 +#, elixir-autogen, elixir-format +msgid "This device" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:532 +#, elixir-autogen, elixir-format, fuzzy +msgid "Unknown device" +msgstr "Inconnu" diff --git a/priv/gettext/it/LC_MESSAGES/default.po b/priv/gettext/it/LC_MESSAGES/default.po index e2aaa32d8..319e90288 100644 --- a/priv/gettext/it/LC_MESSAGES/default.po +++ b/priv/gettext/it/LC_MESSAGES/default.po @@ -168,6 +168,7 @@ msgstr "" msgid "Registered accounts" msgstr "" +#: lib/phoenix_kit_web/live/components/user_settings.ex:1247 #: lib/phoenix_kit_web/live/dashboard.html.heex:170 #: lib/phoenix_kit_web/live/dashboard.html.heex:175 #: lib/phoenix_kit_web/live/users/sessions.html.heex:18 @@ -427,7 +428,7 @@ msgstr "" msgid "%{language} (Published)" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:383 +#: lib/phoenix_kit_web/live/components/user_settings.ex:403 #, elixir-autogen, elixir-format msgid "%{provider} account disconnected successfully" msgstr "" @@ -447,7 +448,7 @@ msgstr "" msgid "123 Business Street" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:196 +#: lib/phoenix_kit_web/live/components/user_settings.ex:216 #, elixir-autogen, elixir-format msgid "A link to confirm your email change has been sent to the new address." msgstr "" @@ -668,12 +669,12 @@ msgstr "" msgid "Cannot delete your own account" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:417 +#: lib/phoenix_kit_web/live/components/user_settings.ex:437 #, elixir-autogen, elixir-format msgid "Cannot disconnect %{provider}. Please ensure you have at least one sign-in method available." msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:412 +#: lib/phoenix_kit_web/live/components/user_settings.ex:432 #, elixir-autogen, elixir-format msgid "Cannot disconnect %{provider}. This is your only sign-in method. Please set a password or connect another provider first." msgstr "" @@ -1079,7 +1080,7 @@ msgstr "" msgid "Failed to delete user" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:403 +#: lib/phoenix_kit_web/live/components/user_settings.ex:423 #, elixir-autogen, elixir-format msgid "Failed to disconnect provider. Please try again." msgstr "" @@ -1275,6 +1276,7 @@ msgid "Italic" msgstr "" #: lib/phoenix_kit_web/users/login.html.heex:58 +#: lib/phoenix_kit_web/users/qr_login.html.heex:34 #, elixir-autogen, elixir-format msgid "Keep me logged in" msgstr "" @@ -1532,7 +1534,7 @@ msgstr "Pagina pubblicata con successo" msgid "Password" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:247 +#: lib/phoenix_kit_web/live/components/user_settings.ex:267 #, elixir-autogen, elixir-format msgid "Password changed successfully." msgstr "" @@ -1614,7 +1616,7 @@ msgstr "Preferenze sulla privacy" msgid "Profile" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:319 +#: lib/phoenix_kit_web/live/components/user_settings.ex:339 #, elixir-autogen, elixir-format msgid "Profile updated successfully" msgstr "" @@ -1630,7 +1632,7 @@ msgstr "" msgid "Protected core roles" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:393 +#: lib/phoenix_kit_web/live/components/user_settings.ex:413 #, elixir-autogen, elixir-format msgid "Provider not found" msgstr "" @@ -3945,7 +3947,7 @@ msgstr "" msgid "Authorize access to your %{provider} account. This will open a sign-in page where you choose which account to connect." msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:76 +#: lib/phoenix_kit_web/live/components/user_settings.ex:89 #, elixir-autogen, elixir-format, fuzzy msgid "Avatar updated successfully!" msgstr "" @@ -4380,7 +4382,7 @@ msgstr "" msgid "Failed to save" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:473 +#: lib/phoenix_kit_web/live/components/user_settings.ex:493 #, elixir-autogen, elixir-format msgid "Failed to save notification preferences." msgstr "" @@ -4400,7 +4402,7 @@ msgstr "Impossibile salvare le impostazioni" msgid "Failed to toggle maintenance mode" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:83 +#: lib/phoenix_kit_web/live/components/user_settings.ex:96 #, elixir-autogen, elixir-format, fuzzy msgid "Failed to update avatar" msgstr "" @@ -4661,7 +4663,7 @@ msgstr "" msgid "Maintenance schedule saved" msgstr "" -#: lib/phoenix_kit_web/live/dashboard/settings.ex:55 +#: lib/phoenix_kit_web/live/dashboard/settings.ex:58 #, elixir-autogen, elixir-format msgid "Manage your account settings and preferences" msgstr "" @@ -4825,12 +4827,12 @@ msgstr "" msgid "Not tested" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:466 +#: lib/phoenix_kit_web/live/components/user_settings.ex:486 #, elixir-autogen, elixir-format msgid "Notification preferences saved." msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1113 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1184 #: lib/phoenix_kit_web/live/modules.html.heex:667 #: lib/phoenix_kit_web/live/modules/notifications/index.html.heex:5 #: lib/phoenix_kit_web/live/settings.html.heex:318 @@ -4916,7 +4918,7 @@ msgstr "" msgid "Person" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1124 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1195 #, elixir-autogen, elixir-format msgid "Pick which notification types you want to receive. Unchecked types are muted — activities still record in the audit log but no bell notification is created for you." msgstr "" @@ -5007,7 +5009,7 @@ msgstr "" msgid "Save Tax Settings" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1161 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1232 #, elixir-autogen, elixir-format, fuzzy msgid "Save preferences" msgstr "Salva preferenze" @@ -10141,12 +10143,12 @@ msgstr "" msgid "Approve this sign-in?" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:125 +#: lib/phoenix_kit_web/users/qr_login.ex:153 #, elixir-autogen, elixir-format msgid "Approved — signing you in…" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:37 +#: lib/phoenix_kit_web/users/qr_login.html.heex:49 #, elixir-autogen, elixir-format msgid "Back to sign in" msgstr "" @@ -10215,12 +10217,12 @@ msgstr "" msgid "Only approve if this is you. Approving signs that browser in to your account." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:27 +#: lib/phoenix_kit_web/users/qr_login.html.heex:39 #, elixir-autogen, elixir-format msgid "Open the camera on your signed-in phone." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:28 +#: lib/phoenix_kit_web/users/qr_login.html.heex:40 #, elixir-autogen, elixir-format msgid "Point it at the code above." msgstr "" @@ -10235,7 +10237,7 @@ msgstr "" msgid "QR Code" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:37 +#: lib/phoenix_kit_web/users/qr_login.ex:42 #: lib/phoenix_kit_web/users/qr_login_confirm.ex:32 #: lib/phoenix_kit_web/users/qr_login_confirm.ex:76 #, elixir-autogen, elixir-format @@ -10252,12 +10254,12 @@ msgstr "" msgid "Requested" msgstr "Obbligatorio" -#: lib/phoenix_kit_web/users/qr_login.ex:122 +#: lib/phoenix_kit_web/users/qr_login.ex:150 #, elixir-autogen, elixir-format msgid "Scan this code with your phone's camera, then approve the sign-in on your phone." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:128 +#: lib/phoenix_kit_web/users/qr_login.ex:156 #, elixir-autogen, elixir-format msgid "Show a new code" msgstr "" @@ -10274,28 +10276,28 @@ msgstr "" msgid "Signed in" msgstr "" -#: lib/phoenix_kit_web/users/qr_login_complete.ex:26 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:29 #, elixir-autogen, elixir-format msgid "Signed in with QR code." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:29 +#: lib/phoenix_kit_web/users/qr_login.html.heex:41 #, elixir-autogen, elixir-format msgid "Tap Approve on your phone to finish signing in here." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:126 +#: lib/phoenix_kit_web/users/qr_login.ex:154 #, elixir-autogen, elixir-format msgid "The sign-in request was denied." msgstr "" -#: lib/phoenix_kit_web/users/qr_login_complete.ex:31 -#: lib/phoenix_kit_web/users/qr_login_complete.ex:38 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:34 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:41 #, elixir-autogen, elixir-format msgid "This QR sign-in link is invalid or has expired." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:127 +#: lib/phoenix_kit_web/users/qr_login.ex:155 #, elixir-autogen, elixir-format msgid "This code expired." msgstr "" @@ -10384,7 +10386,7 @@ msgstr "" msgid "Or add via" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:56 +#: lib/phoenix_kit_web/users/qr_login.ex:61 #, elixir-autogen, elixir-format msgid "Too many attempts. Please try again shortly." msgstr "" @@ -10498,3 +10500,78 @@ msgstr "" #, elixir-autogen, elixir-format msgid "xAI requires a funded account before the API will return completions" msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:547 +#, elixir-autogen, elixir-format +msgid "Active %{count} days ago" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:540 +#, elixir-autogen, elixir-format, fuzzy +msgid "Active now" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:545 +#, elixir-autogen, elixir-format, fuzzy +msgid "Active today" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:546 +#, elixir-autogen, elixir-format +msgid "Active yesterday" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1258 +#, elixir-autogen, elixir-format +msgid "Devices currently signed in to your account. If you don't recognize one, sign it out." +msgstr "" + +#: lib/phoenix_kit/users/login_alerts.ex:140 +#, elixir-autogen, elixir-format +msgid "New sign-in to your account from %{details}." +msgstr "" + +#: lib/phoenix_kit/users/login_alerts.ex:139 +#, elixir-autogen, elixir-format +msgid "New sign-in to your account." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1291 +#, elixir-autogen, elixir-format, fuzzy +msgid "Sign out" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1302 +#, elixir-autogen, elixir-format +msgid "Sign out of all other sessions?" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1305 +#, elixir-autogen, elixir-format +msgid "Sign out other sessions" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:520 +#, elixir-autogen, elixir-format +msgid "Signed out of all other sessions." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:503 +#, elixir-autogen, elixir-format +msgid "Signed out of that session." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:504 +#, elixir-autogen, elixir-format +msgid "That session is no longer active." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1275 +#, elixir-autogen, elixir-format +msgid "This device" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:532 +#, elixir-autogen, elixir-format, fuzzy +msgid "Unknown device" +msgstr "" diff --git a/priv/gettext/pl/LC_MESSAGES/default.po b/priv/gettext/pl/LC_MESSAGES/default.po index f726253ce..6968afe3c 100644 --- a/priv/gettext/pl/LC_MESSAGES/default.po +++ b/priv/gettext/pl/LC_MESSAGES/default.po @@ -168,6 +168,7 @@ msgstr "" msgid "Registered accounts" msgstr "" +#: lib/phoenix_kit_web/live/components/user_settings.ex:1247 #: lib/phoenix_kit_web/live/dashboard.html.heex:170 #: lib/phoenix_kit_web/live/dashboard.html.heex:175 #: lib/phoenix_kit_web/live/users/sessions.html.heex:18 @@ -427,7 +428,7 @@ msgstr "" msgid "%{language} (Published)" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:383 +#: lib/phoenix_kit_web/live/components/user_settings.ex:403 #, elixir-autogen, elixir-format msgid "%{provider} account disconnected successfully" msgstr "" @@ -447,7 +448,7 @@ msgstr "" msgid "123 Business Street" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:196 +#: lib/phoenix_kit_web/live/components/user_settings.ex:216 #, elixir-autogen, elixir-format msgid "A link to confirm your email change has been sent to the new address." msgstr "" @@ -668,12 +669,12 @@ msgstr "" msgid "Cannot delete your own account" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:417 +#: lib/phoenix_kit_web/live/components/user_settings.ex:437 #, elixir-autogen, elixir-format msgid "Cannot disconnect %{provider}. Please ensure you have at least one sign-in method available." msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:412 +#: lib/phoenix_kit_web/live/components/user_settings.ex:432 #, elixir-autogen, elixir-format msgid "Cannot disconnect %{provider}. This is your only sign-in method. Please set a password or connect another provider first." msgstr "" @@ -1079,7 +1080,7 @@ msgstr "" msgid "Failed to delete user" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:403 +#: lib/phoenix_kit_web/live/components/user_settings.ex:423 #, elixir-autogen, elixir-format msgid "Failed to disconnect provider. Please try again." msgstr "" @@ -1275,6 +1276,7 @@ msgid "Italic" msgstr "" #: lib/phoenix_kit_web/users/login.html.heex:58 +#: lib/phoenix_kit_web/users/qr_login.html.heex:34 #, elixir-autogen, elixir-format msgid "Keep me logged in" msgstr "" @@ -1532,7 +1534,7 @@ msgstr "Strona została opublikowana pomyślnie" msgid "Password" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:247 +#: lib/phoenix_kit_web/live/components/user_settings.ex:267 #, elixir-autogen, elixir-format msgid "Password changed successfully." msgstr "" @@ -1614,7 +1616,7 @@ msgstr "Ustawienia prywatności" msgid "Profile" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:319 +#: lib/phoenix_kit_web/live/components/user_settings.ex:339 #, elixir-autogen, elixir-format msgid "Profile updated successfully" msgstr "" @@ -1630,7 +1632,7 @@ msgstr "" msgid "Protected core roles" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:393 +#: lib/phoenix_kit_web/live/components/user_settings.ex:413 #, elixir-autogen, elixir-format msgid "Provider not found" msgstr "" @@ -3945,7 +3947,7 @@ msgstr "" msgid "Authorize access to your %{provider} account. This will open a sign-in page where you choose which account to connect." msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:76 +#: lib/phoenix_kit_web/live/components/user_settings.ex:89 #, elixir-autogen, elixir-format, fuzzy msgid "Avatar updated successfully!" msgstr "" @@ -4380,7 +4382,7 @@ msgstr "" msgid "Failed to save" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:473 +#: lib/phoenix_kit_web/live/components/user_settings.ex:493 #, elixir-autogen, elixir-format msgid "Failed to save notification preferences." msgstr "" @@ -4400,7 +4402,7 @@ msgstr "Nie udało się zapisać ustawień" msgid "Failed to toggle maintenance mode" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:83 +#: lib/phoenix_kit_web/live/components/user_settings.ex:96 #, elixir-autogen, elixir-format, fuzzy msgid "Failed to update avatar" msgstr "" @@ -4661,7 +4663,7 @@ msgstr "" msgid "Maintenance schedule saved" msgstr "" -#: lib/phoenix_kit_web/live/dashboard/settings.ex:55 +#: lib/phoenix_kit_web/live/dashboard/settings.ex:58 #, elixir-autogen, elixir-format msgid "Manage your account settings and preferences" msgstr "" @@ -4825,12 +4827,12 @@ msgstr "" msgid "Not tested" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:466 +#: lib/phoenix_kit_web/live/components/user_settings.ex:486 #, elixir-autogen, elixir-format msgid "Notification preferences saved." msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1113 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1184 #: lib/phoenix_kit_web/live/modules.html.heex:667 #: lib/phoenix_kit_web/live/modules/notifications/index.html.heex:5 #: lib/phoenix_kit_web/live/settings.html.heex:318 @@ -4916,7 +4918,7 @@ msgstr "" msgid "Person" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1124 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1195 #, elixir-autogen, elixir-format msgid "Pick which notification types you want to receive. Unchecked types are muted — activities still record in the audit log but no bell notification is created for you." msgstr "" @@ -5007,7 +5009,7 @@ msgstr "" msgid "Save Tax Settings" msgstr "" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1161 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1232 #, elixir-autogen, elixir-format, fuzzy msgid "Save preferences" msgstr "Zapisz ustawienia" @@ -10167,12 +10169,12 @@ msgstr "" msgid "Approve this sign-in?" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:125 +#: lib/phoenix_kit_web/users/qr_login.ex:153 #, elixir-autogen, elixir-format msgid "Approved — signing you in…" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:37 +#: lib/phoenix_kit_web/users/qr_login.html.heex:49 #, elixir-autogen, elixir-format msgid "Back to sign in" msgstr "" @@ -10241,12 +10243,12 @@ msgstr "" msgid "Only approve if this is you. Approving signs that browser in to your account." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:27 +#: lib/phoenix_kit_web/users/qr_login.html.heex:39 #, elixir-autogen, elixir-format msgid "Open the camera on your signed-in phone." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:28 +#: lib/phoenix_kit_web/users/qr_login.html.heex:40 #, elixir-autogen, elixir-format msgid "Point it at the code above." msgstr "" @@ -10261,7 +10263,7 @@ msgstr "" msgid "QR Code" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:37 +#: lib/phoenix_kit_web/users/qr_login.ex:42 #: lib/phoenix_kit_web/users/qr_login_confirm.ex:32 #: lib/phoenix_kit_web/users/qr_login_confirm.ex:76 #, elixir-autogen, elixir-format @@ -10278,12 +10280,12 @@ msgstr "" msgid "Requested" msgstr "Wymagane" -#: lib/phoenix_kit_web/users/qr_login.ex:122 +#: lib/phoenix_kit_web/users/qr_login.ex:150 #, elixir-autogen, elixir-format msgid "Scan this code with your phone's camera, then approve the sign-in on your phone." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:128 +#: lib/phoenix_kit_web/users/qr_login.ex:156 #, elixir-autogen, elixir-format msgid "Show a new code" msgstr "" @@ -10300,28 +10302,28 @@ msgstr "" msgid "Signed in" msgstr "" -#: lib/phoenix_kit_web/users/qr_login_complete.ex:26 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:29 #, elixir-autogen, elixir-format msgid "Signed in with QR code." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:29 +#: lib/phoenix_kit_web/users/qr_login.html.heex:41 #, elixir-autogen, elixir-format msgid "Tap Approve on your phone to finish signing in here." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:126 +#: lib/phoenix_kit_web/users/qr_login.ex:154 #, elixir-autogen, elixir-format msgid "The sign-in request was denied." msgstr "" -#: lib/phoenix_kit_web/users/qr_login_complete.ex:31 -#: lib/phoenix_kit_web/users/qr_login_complete.ex:38 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:34 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:41 #, elixir-autogen, elixir-format msgid "This QR sign-in link is invalid or has expired." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:127 +#: lib/phoenix_kit_web/users/qr_login.ex:155 #, elixir-autogen, elixir-format msgid "This code expired." msgstr "" @@ -10410,7 +10412,7 @@ msgstr "" msgid "Or add via" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:56 +#: lib/phoenix_kit_web/users/qr_login.ex:61 #, elixir-autogen, elixir-format msgid "Too many attempts. Please try again shortly." msgstr "" @@ -10524,3 +10526,78 @@ msgstr "" #, elixir-autogen, elixir-format msgid "xAI requires a funded account before the API will return completions" msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:547 +#, elixir-autogen, elixir-format +msgid "Active %{count} days ago" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:540 +#, elixir-autogen, elixir-format, fuzzy +msgid "Active now" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:545 +#, elixir-autogen, elixir-format, fuzzy +msgid "Active today" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:546 +#, elixir-autogen, elixir-format +msgid "Active yesterday" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1258 +#, elixir-autogen, elixir-format +msgid "Devices currently signed in to your account. If you don't recognize one, sign it out." +msgstr "" + +#: lib/phoenix_kit/users/login_alerts.ex:140 +#, elixir-autogen, elixir-format +msgid "New sign-in to your account from %{details}." +msgstr "" + +#: lib/phoenix_kit/users/login_alerts.ex:139 +#, elixir-autogen, elixir-format +msgid "New sign-in to your account." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1291 +#, elixir-autogen, elixir-format, fuzzy +msgid "Sign out" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1302 +#, elixir-autogen, elixir-format +msgid "Sign out of all other sessions?" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1305 +#, elixir-autogen, elixir-format +msgid "Sign out other sessions" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:520 +#, elixir-autogen, elixir-format +msgid "Signed out of all other sessions." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:503 +#, elixir-autogen, elixir-format +msgid "Signed out of that session." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:504 +#, elixir-autogen, elixir-format +msgid "That session is no longer active." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1275 +#, elixir-autogen, elixir-format +msgid "This device" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:532 +#, elixir-autogen, elixir-format, fuzzy +msgid "Unknown device" +msgstr "" diff --git a/priv/gettext/ru/LC_MESSAGES/default.po b/priv/gettext/ru/LC_MESSAGES/default.po index 94777ac9d..9f61cbed1 100644 --- a/priv/gettext/ru/LC_MESSAGES/default.po +++ b/priv/gettext/ru/LC_MESSAGES/default.po @@ -170,6 +170,7 @@ msgstr "Всего пользователей" msgid "Registered accounts" msgstr "Зарегистрированные аккаунты" +#: lib/phoenix_kit_web/live/components/user_settings.ex:1247 #: lib/phoenix_kit_web/live/dashboard.html.heex:170 #: lib/phoenix_kit_web/live/dashboard.html.heex:175 #: lib/phoenix_kit_web/live/users/sessions.html.heex:18 @@ -430,7 +431,7 @@ msgstr "%{language} (Черновик)" msgid "%{language} (Published)" msgstr "%{language} (Опубликовано)" -#: lib/phoenix_kit_web/live/components/user_settings.ex:383 +#: lib/phoenix_kit_web/live/components/user_settings.ex:403 #, elixir-autogen, elixir-format msgid "%{provider} account disconnected successfully" msgstr "Аккаунт %{provider} успешно отключён" @@ -450,7 +451,7 @@ msgstr "(необязательно)" msgid "123 Business Street" msgstr "ул. Примерная, 123" -#: lib/phoenix_kit_web/live/components/user_settings.ex:196 +#: lib/phoenix_kit_web/live/components/user_settings.ex:216 #, elixir-autogen, elixir-format msgid "A link to confirm your email change has been sent to the new address." msgstr "Ссылка для подтверждения смены email отправлена на новый адрес." @@ -671,12 +672,12 @@ msgstr "Невозможно удалить последнего системн msgid "Cannot delete your own account" msgstr "Невозможно удалить свой собственный аккаунт" -#: lib/phoenix_kit_web/live/components/user_settings.ex:417 +#: lib/phoenix_kit_web/live/components/user_settings.ex:437 #, elixir-autogen, elixir-format msgid "Cannot disconnect %{provider}. Please ensure you have at least one sign-in method available." msgstr "Невозможно отключить %{provider}. Убедитесь, что у вас есть хотя бы один способ входа." -#: lib/phoenix_kit_web/live/components/user_settings.ex:412 +#: lib/phoenix_kit_web/live/components/user_settings.ex:432 #, elixir-autogen, elixir-format msgid "Cannot disconnect %{provider}. This is your only sign-in method. Please set a password or connect another provider first." msgstr "Невозможно отключить %{provider}. Это ваш единственный способ входа. Сначала установите пароль или подключите другой провайдер." @@ -1082,7 +1083,7 @@ msgstr "Не удалось удалить роль" msgid "Failed to delete user" msgstr "Не удалось удалить пользователя" -#: lib/phoenix_kit_web/live/components/user_settings.ex:403 +#: lib/phoenix_kit_web/live/components/user_settings.ex:423 #, elixir-autogen, elixir-format msgid "Failed to disconnect provider. Please try again." msgstr "Не удалось отключить провайдер. Попробуйте ещё раз." @@ -1278,6 +1279,7 @@ msgid "Italic" msgstr "Курсив" #: lib/phoenix_kit_web/users/login.html.heex:58 +#: lib/phoenix_kit_web/users/qr_login.html.heex:34 #, elixir-autogen, elixir-format msgid "Keep me logged in" msgstr "Оставаться в системе" @@ -1535,7 +1537,7 @@ msgstr "Страница успешно опубликована" msgid "Password" msgstr "Пароль" -#: lib/phoenix_kit_web/live/components/user_settings.ex:247 +#: lib/phoenix_kit_web/live/components/user_settings.ex:267 #, elixir-autogen, elixir-format msgid "Password changed successfully." msgstr "Пароль успешно изменён." @@ -1617,7 +1619,7 @@ msgstr "Настройки конфиденциальности" msgid "Profile" msgstr "Профиль" -#: lib/phoenix_kit_web/live/components/user_settings.ex:319 +#: lib/phoenix_kit_web/live/components/user_settings.ex:339 #, elixir-autogen, elixir-format msgid "Profile updated successfully" msgstr "Профиль успешно обновлён" @@ -1633,7 +1635,7 @@ msgstr "Защищённая" msgid "Protected core roles" msgstr "Защищённые системные роли" -#: lib/phoenix_kit_web/live/components/user_settings.ex:393 +#: lib/phoenix_kit_web/live/components/user_settings.ex:413 #, elixir-autogen, elixir-format msgid "Provider not found" msgstr "Провайдер не найден" @@ -3953,7 +3955,7 @@ msgstr "Авторизация не удалась: %{reason}" msgid "Authorize access to your %{provider} account. This will open a sign-in page where you choose which account to connect." msgstr "Авторизуйте доступ к вашему аккаунту %{provider}. Откроется страница входа, где вы выберете аккаунт для подключения." -#: lib/phoenix_kit_web/live/components/user_settings.ex:76 +#: lib/phoenix_kit_web/live/components/user_settings.ex:89 #, elixir-autogen, elixir-format, fuzzy msgid "Avatar updated successfully!" msgstr "Аватар успешно обновлён!" @@ -4388,7 +4390,7 @@ msgstr "Не удалось удалить участника" msgid "Failed to save" msgstr "Не удалось сохранить" -#: lib/phoenix_kit_web/live/components/user_settings.ex:473 +#: lib/phoenix_kit_web/live/components/user_settings.ex:493 #, elixir-autogen, elixir-format msgid "Failed to save notification preferences." msgstr "Не удалось сохранить настройки уведомлений." @@ -4408,7 +4410,7 @@ msgstr "Не удалось отправить приглашение" msgid "Failed to toggle maintenance mode" msgstr "Не удалось переключить режим обслуживания" -#: lib/phoenix_kit_web/live/components/user_settings.ex:83 +#: lib/phoenix_kit_web/live/components/user_settings.ex:96 #, elixir-autogen, elixir-format, fuzzy msgid "Failed to update avatar" msgstr "Не удалось обновить аватар" @@ -4669,7 +4671,7 @@ msgstr "Расписание обслуживания очищено" msgid "Maintenance schedule saved" msgstr "Расписание обслуживания сохранено" -#: lib/phoenix_kit_web/live/dashboard/settings.ex:55 +#: lib/phoenix_kit_web/live/dashboard/settings.ex:58 #, elixir-autogen, elixir-format msgid "Manage your account settings and preferences" msgstr "Управляйте настройками и параметрами вашего аккаунта" @@ -4833,12 +4835,12 @@ msgstr "Не подключено" msgid "Not tested" msgstr "Не проверено" -#: lib/phoenix_kit_web/live/components/user_settings.ex:466 +#: lib/phoenix_kit_web/live/components/user_settings.ex:486 #, elixir-autogen, elixir-format msgid "Notification preferences saved." msgstr "Настройки уведомлений сохранены." -#: lib/phoenix_kit_web/live/components/user_settings.ex:1113 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1184 #: lib/phoenix_kit_web/live/modules.html.heex:667 #: lib/phoenix_kit_web/live/modules/notifications/index.html.heex:5 #: lib/phoenix_kit_web/live/settings.html.heex:318 @@ -4924,7 +4926,7 @@ msgstr "Персональный почтовый ящик, управляемы msgid "Person" msgstr "Физическое лицо" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1124 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1195 #, elixir-autogen, elixir-format msgid "Pick which notification types you want to receive. Unchecked types are muted — activities still record in the audit log but no bell notification is created for you." msgstr "Выберите типы уведомлений, которые хотите получать. Отключённые типы заглушены — действия по-прежнему записываются в журнал аудита, но для вас не создаются уведомления колокольчика." @@ -5015,7 +5017,7 @@ msgstr "Сохранить расписание" msgid "Save Tax Settings" msgstr "Сохранить налоговые настройки" -#: lib/phoenix_kit_web/live/components/user_settings.ex:1161 +#: lib/phoenix_kit_web/live/components/user_settings.ex:1232 #, elixir-autogen, elixir-format, fuzzy msgid "Save preferences" msgstr "Сохранить настройки" @@ -10177,12 +10179,12 @@ msgstr "Вход через Apple" msgid "Approve this sign-in?" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:125 +#: lib/phoenix_kit_web/users/qr_login.ex:153 #, elixir-autogen, elixir-format msgid "Approved — signing you in…" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:37 +#: lib/phoenix_kit_web/users/qr_login.html.heex:49 #, elixir-autogen, elixir-format msgid "Back to sign in" msgstr "" @@ -10251,12 +10253,12 @@ msgstr "" msgid "Only approve if this is you. Approving signs that browser in to your account." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:27 +#: lib/phoenix_kit_web/users/qr_login.html.heex:39 #, elixir-autogen, elixir-format msgid "Open the camera on your signed-in phone." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:28 +#: lib/phoenix_kit_web/users/qr_login.html.heex:40 #, elixir-autogen, elixir-format msgid "Point it at the code above." msgstr "" @@ -10271,7 +10273,7 @@ msgstr "" msgid "QR Code" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:37 +#: lib/phoenix_kit_web/users/qr_login.ex:42 #: lib/phoenix_kit_web/users/qr_login_confirm.ex:32 #: lib/phoenix_kit_web/users/qr_login_confirm.ex:76 #, elixir-autogen, elixir-format @@ -10288,12 +10290,12 @@ msgstr "" msgid "Requested" msgstr "Обязательно" -#: lib/phoenix_kit_web/users/qr_login.ex:122 +#: lib/phoenix_kit_web/users/qr_login.ex:150 #, elixir-autogen, elixir-format msgid "Scan this code with your phone's camera, then approve the sign-in on your phone." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:128 +#: lib/phoenix_kit_web/users/qr_login.ex:156 #, elixir-autogen, elixir-format msgid "Show a new code" msgstr "" @@ -10311,28 +10313,28 @@ msgstr "Войти с паролем" msgid "Signed in" msgstr "Войти" -#: lib/phoenix_kit_web/users/qr_login_complete.ex:26 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:29 #, elixir-autogen, elixir-format msgid "Signed in with QR code." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.html.heex:29 +#: lib/phoenix_kit_web/users/qr_login.html.heex:41 #, elixir-autogen, elixir-format msgid "Tap Approve on your phone to finish signing in here." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:126 +#: lib/phoenix_kit_web/users/qr_login.ex:154 #, elixir-autogen, elixir-format msgid "The sign-in request was denied." msgstr "" -#: lib/phoenix_kit_web/users/qr_login_complete.ex:31 -#: lib/phoenix_kit_web/users/qr_login_complete.ex:38 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:34 +#: lib/phoenix_kit_web/users/qr_login_complete.ex:41 #, elixir-autogen, elixir-format msgid "This QR sign-in link is invalid or has expired." msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:127 +#: lib/phoenix_kit_web/users/qr_login.ex:155 #, elixir-autogen, elixir-format msgid "This code expired." msgstr "" @@ -10421,7 +10423,7 @@ msgstr "" msgid "Or add via" msgstr "" -#: lib/phoenix_kit_web/users/qr_login.ex:56 +#: lib/phoenix_kit_web/users/qr_login.ex:61 #, elixir-autogen, elixir-format msgid "Too many attempts. Please try again shortly." msgstr "" @@ -10535,3 +10537,79 @@ msgstr "" #, elixir-autogen, elixir-format msgid "xAI requires a funded account before the API will return completions" msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:547 +#, elixir-autogen, elixir-format +msgid "Active %{count} days ago" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:540 +#, elixir-autogen, elixir-format, fuzzy +msgid "Active now" +msgstr "Активно" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:545 +#, elixir-autogen, elixir-format, fuzzy +msgid "Active today" +msgstr "Активировать" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:546 +#, elixir-autogen, elixir-format +msgid "Active yesterday" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1258 +#, elixir-autogen, elixir-format +msgid "Devices currently signed in to your account. If you don't recognize one, sign it out." +msgstr "" + +#: lib/phoenix_kit/users/login_alerts.ex:140 +#, elixir-autogen, elixir-format +msgid "New sign-in to your account from %{details}." +msgstr "" + +#: lib/phoenix_kit/users/login_alerts.ex:139 +#, elixir-autogen, elixir-format +msgid "New sign-in to your account." +msgstr "" + +## Login page translations +#: lib/phoenix_kit_web/live/components/user_settings.ex:1291 +#, elixir-autogen, elixir-format, fuzzy +msgid "Sign out" +msgstr "Войти" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1302 +#, elixir-autogen, elixir-format +msgid "Sign out of all other sessions?" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1305 +#, elixir-autogen, elixir-format +msgid "Sign out other sessions" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:520 +#, elixir-autogen, elixir-format +msgid "Signed out of all other sessions." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:503 +#, elixir-autogen, elixir-format +msgid "Signed out of that session." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:504 +#, elixir-autogen, elixir-format +msgid "That session is no longer active." +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:1275 +#, elixir-autogen, elixir-format +msgid "This device" +msgstr "" + +#: lib/phoenix_kit_web/live/components/user_settings.ex:532 +#, elixir-autogen, elixir-format, fuzzy +msgid "Unknown device" +msgstr "Неизвестно" diff --git a/test/phoenix_kit/users/qr_login_test.exs b/test/phoenix_kit/users/qr_login_test.exs new file mode 100644 index 000000000..7c09115e0 --- /dev/null +++ b/test/phoenix_kit/users/qr_login_test.exs @@ -0,0 +1,60 @@ +defmodule PhoenixKit.Users.QrLoginTest do + @moduledoc """ + Covers the PhoenixKit-side QR device-handoff wrapper end to end: the + mint → approve → consume rendezvous (routed through PhoenixKit's internal + PubSub), single-use enforcement, and the best-effort location formatter. + + `:phoenix_kit_internal_pubsub` is started by the test helper; the keyfob + ETS store is started per-test (async: false — it's a named process). + """ + use ExUnit.Case, async: false + + alias PhoenixKit.Users.QrLogin + + setup do + start_supervised!(Keyfob.Store.ETS) + :ok + end + + describe "create_request/approve/consume" do + test "approve broadcasts a login token that consumes to the user, exactly once" do + {:ok, %{token: token}} = QrLogin.create_request(meta: %{browser: "Chrome"}) + :ok = QrLogin.subscribe(token) + + assert {:ok, %{state: :pending, meta: %{browser: "Chrome"}}} = QrLogin.peek(token) + + assert :ok = QrLogin.approve(token, "user-uuid-1") + assert_receive {:keyfob, ^token, {:approved, login_token}} + + # The QR-borne request token is NOT a credential — only the minted + # login token consumes, and only once. + refute login_token == token + assert {:ok, "user-uuid-1"} = QrLogin.consume(login_token) + assert {:error, :not_found} = QrLogin.consume(login_token) + end + + test "the request token itself cannot be consumed" do + {:ok, %{token: token}} = QrLogin.create_request() + :ok = QrLogin.approve(token, "user-uuid-2") + assert {:error, :not_found} = QrLogin.consume(token) + end + + test "deny broadcasts and prevents later approval" do + {:ok, %{token: token}} = QrLogin.create_request() + :ok = QrLogin.subscribe(token) + + assert :ok = QrLogin.deny(token) + assert_receive {:keyfob, ^token, :denied} + assert {:error, :not_found} = QrLogin.approve(token, "user-uuid-3") + end + end + + describe "location_for/1" do + test "returns nil for nil and non-routable/placeholder IPs (no network call)" do + assert QrLogin.location_for(nil) == nil + assert QrLogin.location_for("") == nil + assert QrLogin.location_for("unknown") == nil + assert QrLogin.location_for("127.0.0.1") == nil + end + end +end diff --git a/test/phoenix_kit/users/sessions_device_test.exs b/test/phoenix_kit/users/sessions_device_test.exs new file mode 100644 index 000000000..d317b0ab3 --- /dev/null +++ b/test/phoenix_kit/users/sessions_device_test.exs @@ -0,0 +1,123 @@ +defmodule PhoenixKit.Users.SessionsDeviceTest do + @moduledoc """ + Integration tests for the self-service Active Sessions surface: + device-enriched listing, the current-session flag, and the user-scoped + revoke functions (which must never let one user revoke another's session). + """ + use PhoenixKit.DataCase, async: true + + alias PhoenixKit.RepoHelper, as: Repo + alias PhoenixKit.Users.Auth + alias PhoenixKit.Users.Auth.KnownDevice + alias PhoenixKit.Users.Sessions + alias PhoenixKit.Utils.SessionFingerprint + + defp user_fixture(email) do + {:ok, user} = Auth.register_user(%{email: email, password: "ValidPassword123!"}) + user + end + + defp session_token(user, ip, ua_hash) do + fp = %SessionFingerprint{ip_address: ip, user_agent_hash: ua_hash} + Auth.generate_user_session_token(user, fingerprint: fp) + end + + defp known_device(user, ip, ua_hash, extra) do + now = DateTime.utc_now() |> DateTime.truncate(:second) + + %KnownDevice{} + |> KnownDevice.changeset( + Map.merge( + %{ + user_uuid: user.uuid, + ip_address: ip, + user_agent_hash: ua_hash, + first_seen_at: now, + last_seen_at: now + }, + extra + ) + ) + |> Repo.insert!() + end + + describe "list_user_device_sessions/2" do + test "enriches from known devices and flags the current session" do + user = user_fixture("qr-sessions-list@example.com") + ua = String.duplicate("a", 64) + current = session_token(user, "203.0.113.1", ua) + _other = session_token(user, "203.0.113.2", String.duplicate("b", 64)) + + known_device(user, "203.0.113.1", ua, %{ + browser: "Chrome", + os: "macOS", + location: "Berlin, DE" + }) + + sessions = Sessions.list_user_device_sessions(user, current) + assert length(sessions) == 2 + + cur = Enum.find(sessions, & &1.is_current) + assert cur.browser == "Chrome" + assert cur.os == "macOS" + assert cur.location == "Berlin, DE" + assert cur.ip_address == "203.0.113.1" + + # A session with no matching known-device row degrades gracefully. + other = Enum.find(sessions, &(not &1.is_current)) + assert other.browser == nil + assert other.location == nil + end + + test "no session is current when the token is unknown" do + user = user_fixture("qr-sessions-nocurrent@example.com") + _t = session_token(user, "203.0.113.3", String.duplicate("c", 64)) + + sessions = Sessions.list_user_device_sessions(user, "not-a-real-token") + assert Enum.all?(sessions, &(not &1.is_current)) + end + end + + describe "revoke_user_session/2" do + test "revokes the owner's session but not another user's" do + user = user_fixture("qr-sessions-owner@example.com") + intruder = user_fixture("qr-sessions-intruder@example.com") + token = session_token(user, "203.0.113.5", String.duplicate("d", 64)) + + [%{token_uuid: uuid}] = Sessions.list_user_device_sessions(user, token) + + # Cross-user revoke is refused even with the correct token uuid. + assert {:error, :not_found} = Sessions.revoke_user_session(intruder, uuid) + assert length(Sessions.list_user_device_sessions(user, token)) == 1 + + # The owner can revoke it. + assert :ok = Sessions.revoke_user_session(user, uuid) + assert Sessions.list_user_device_sessions(user, token) == [] + end + end + + describe "revoke_other_user_sessions/2" do + test "keeps the current session and revokes the rest" do + user = user_fixture("qr-sessions-others@example.com") + current = session_token(user, "203.0.113.7", String.duplicate("e", 64)) + _s2 = session_token(user, "203.0.113.8", String.duplicate("f", 64)) + _s3 = session_token(user, "203.0.113.9", String.duplicate("0", 64)) + + assert length(Sessions.list_user_device_sessions(user, current)) == 3 + + assert Sessions.revoke_other_user_sessions(user, current) == 2 + + remaining = Sessions.list_user_device_sessions(user, current) + assert [%{is_current: true}] = remaining + end + + test "with a nil token revokes every session" do + user = user_fixture("qr-sessions-nil@example.com") + _s1 = session_token(user, "203.0.113.10", String.duplicate("a", 64)) + _s2 = session_token(user, "203.0.113.11", String.duplicate("b", 64)) + + assert Sessions.revoke_other_user_sessions(user, nil) == 2 + assert Sessions.list_user_device_sessions(user, nil) == [] + end + end +end