diff --git a/assets/css/app.css b/assets/css/app.css index 14e9ec1..e3c7ee4 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -119,6 +119,8 @@ color: oklch(58% 0.233 277.117) !important; } +/* Input field dark mode fixes */ + /* Marker cluster styles */ .marker-cluster-small { background-color: rgba(16, 185, 129, 0.8); @@ -326,4 +328,60 @@ .historical-dot-marker { z-index: 15 !important; +} + +/* Input field dark mode fixes - Override DaisyUI CSS variables for dark theme */ + +[data-theme="dark"] { + --color-base-100: #2d3748 !important; + --color-base-200: #1a202c !important; + --color-base-300: #171923 !important; + --color-base-content: #f7fafc !important; + --input-bg: #2d3748; + --input-color: #f7fafc; + --input-border: #4a5568; +} + +/* Ultra-specific selector to override DaisyUI */ +html[data-theme="dark"] .card .input, +html[data-theme="dark"] .card .input-bordered, +html[data-theme="dark"] .card input.input, +html[data-theme="dark"] .card input.input-bordered, +html[data-theme="dark"] .card input[type="email"], +html[data-theme="dark"] .card input[type="password"], +html[data-theme="dark"] .form-control input.input, +html[data-theme="dark"] .form-control input.input-bordered, +html[data-theme="dark"] .form-control input[type="email"], +html[data-theme="dark"] .form-control input[type="password"] { + background-color: #2d3748 !important; + color: #f7fafc !important; + border-color: #4a5568 !important; +} + +html[data-theme="dark"] .card .input:focus, +html[data-theme="dark"] .card .input-bordered:focus, +html[data-theme="dark"] .card input.input:focus, +html[data-theme="dark"] .card input.input-bordered:focus, +html[data-theme="dark"] .card input[type="email"]:focus, +html[data-theme="dark"] .card input[type="password"]:focus, +html[data-theme="dark"] .form-control input.input:focus, +html[data-theme="dark"] .form-control input.input-bordered:focus, +html[data-theme="dark"] .form-control input[type="email"]:focus, +html[data-theme="dark"] .form-control input[type="password"]:focus { + background-color: #2d3748 !important; + color: #f7fafc !important; + border-color: #6366f1 !important; +} + +html[data-theme="dark"] .input::placeholder, +html[data-theme="dark"] input.input::placeholder, +html[data-theme="dark"] input[type="email"]::placeholder, +html[data-theme="dark"] input[type="password"]::placeholder { + color: oklch(60% 0.029 256.847) !important; +} + +html[data-theme="dark"] .checkbox, +html[data-theme="dark"] input.checkbox { + background-color: var(--input-bg) !important; + border-color: var(--input-border) !important; } \ No newline at end of file diff --git a/lib/aprsme_web/components/core_components.ex b/lib/aprsme_web/components/core_components.ex index f7c5c5a..329651f 100644 --- a/lib/aprsme_web/components/core_components.ex +++ b/lib/aprsme_web/components/core_components.ex @@ -237,7 +237,7 @@ defmodule AprsmeWeb.CoreComponents do def simple_form(assigns) do ~H""" <.form :let={f} for={@for} as={@as} {@rest}> -
+
{render_slot(@inner_block, f)}
{render_slot(action, f)} @@ -442,6 +442,7 @@ defmodule AprsmeWeb.CoreComponents do """ attr :class, :string, default: "" attr :dev_mode, :boolean, default: false + attr :current_user, :any, default: nil def header(assigns) do ~H""" @@ -455,7 +456,7 @@ defmodule AprsmeWeb.CoreComponents do
@@ -750,6 +751,7 @@ defmodule AprsmeWeb.CoreComponents do """ attr :class, :string, default: "" attr :variant, :atom, values: [:horizontal, :vertical], default: :horizontal + attr :current_user, :any, default: nil def navigation(assigns) do ~H""" @@ -764,6 +766,29 @@ defmodule AprsmeWeb.CoreComponents do {gettext("About")} + <%= if @current_user do %> +
  • + <.link navigate="/users/settings" class="text-gray-900 hover:text-gray-700"> + {gettext("Settings")} + +
  • +
  • + <.link href="/users/log_out" method="delete" class="text-gray-900 hover:text-gray-700"> + {gettext("Log out")} + +
  • + <% else %> +
  • + <.link navigate="/users/register" class="text-gray-900 hover:text-gray-700"> + {gettext("Register")} + +
  • +
  • + <.link navigate="/users/log_in" class="text-gray-900 hover:text-gray-700"> + {gettext("Log in")} + +
  • + <% end %> <% else %>
  • <.link navigate="/" class="text-gray-900 hover:text-gray-700">{gettext("Home")}
  • @@ -771,6 +796,29 @@ defmodule AprsmeWeb.CoreComponents do
  • <.link navigate="/about" class="text-gray-900 hover:text-gray-700">{gettext("About")}
  • + <%= if @current_user do %> +
  • + <.link navigate="/users/settings" class="text-gray-900 hover:text-gray-700"> + {gettext("Settings")} + +
  • +
  • + <.link href="/users/log_out" method="delete" class="text-gray-900 hover:text-gray-700"> + {gettext("Log out")} + +
  • + <% else %> +
  • + <.link navigate="/users/register" class="text-gray-900 hover:text-gray-700"> + {gettext("Register")} + +
  • +
  • + <.link navigate="/users/log_in" class="text-gray-900 hover:text-gray-700"> + {gettext("Log in")} + +
  • + <% end %> <% end %> """ end diff --git a/lib/aprsme_web/components/layouts/app.html.heex b/lib/aprsme_web/components/layouts/app.html.heex index 85224c8..8b144d1 100644 --- a/lib/aprsme_web/components/layouts/app.html.heex +++ b/lib/aprsme_web/components/layouts/app.html.heex @@ -200,7 +200,7 @@ } -<.header dev_mode={Application.get_env(:aprsme, :dev_routes, false)} /> +<.header dev_mode={Application.get_env(:aprsme, :dev_routes, false)} current_user={@current_user} />
    {gettext("Navigation")}
    - <.navigation variant={:vertical} class="text-sm" /> + <.navigation variant={:vertical} class="text-sm" current_user={@current_user} /> diff --git a/lib/aprsme_web/live/user_confirmation_instructions_live.ex b/lib/aprsme_web/live/user_confirmation_instructions_live.ex index 6cc0917..30258a1 100644 --- a/lib/aprsme_web/live/user_confirmation_instructions_live.ex +++ b/lib/aprsme_web/live/user_confirmation_instructions_live.ex @@ -6,25 +6,61 @@ defmodule AprsmeWeb.UserConfirmationInstructionsLive do def render(assigns) do ~H""" - <.header /> +
    +
    +
    +
    +
    +

    + Resend confirmation instructions +

    +

    + Enter your email address to receive a new confirmation link +

    - <.simple_form - :let={f} - for={%{}} - as={:user} - id="resend_confirmation_form" - phx-submit="send_instructions" - > - <.input field={{f, :email}} type="email" label="Email" required /> - <:actions> - <.button phx-disable-with="Sending...">Resend confirmation instructions - - + <.simple_form + :let={_f} + for={%{}} + as={:user} + id="resend_confirmation_form" + phx-submit="send_instructions" + > +
    + + +
    -

    - <.link navigate={~p"/users/register"}>Register - | <.link navigate={~p"/users/log_in"}>Log in -

    +
    + +
    + + +
    OR
    + +
    + <.link navigate={~p"/users/register"} class="link link-primary"> + Register + + | + <.link navigate={~p"/users/log_in"} class="link link-primary"> + Log in + +
    +
    +
    +
    +
    +
    """ end diff --git a/lib/aprsme_web/live/user_confirmation_live.ex b/lib/aprsme_web/live/user_confirmation_live.ex index 6d70e08..0366bd1 100644 --- a/lib/aprsme_web/live/user_confirmation_live.ex +++ b/lib/aprsme_web/live/user_confirmation_live.ex @@ -6,20 +6,49 @@ defmodule AprsmeWeb.UserConfirmationLive do def render(%{live_action: :edit} = assigns) do ~H""" -
    - <.header class="text-center" /> +
    +
    +
    +
    +
    +

    Confirm your account

    +

    Click the button below to confirm your account

    - <.simple_form :let={f} for={%{}} as={:user} id="confirmation_form" phx-submit="confirm_account"> - <.input field={{f, :token}} type="hidden" value={@token} /> - <:actions> - <.button phx-disable-with="Confirming..." class="w-full">Confirm my account - - + <.simple_form + :let={_f} + for={%{}} + as={:user} + id="confirmation_form" + phx-submit="confirm_account" + > + -

    - <.link navigate={~p"/users/register"}>Register - | <.link navigate={~p"/users/log_in"}>Log in -

    +
    + +
    + + +
    OR
    + +
    + <.link navigate={~p"/users/register"} class="link link-primary"> + Register + + | + <.link navigate={~p"/users/log_in"} class="link link-primary"> + Log in + +
    +
    +
    +
    +
    """ end diff --git a/lib/aprsme_web/live/user_forgot_password_live.ex b/lib/aprsme_web/live/user_forgot_password_live.ex index 1bb8b05..ed086e6 100644 --- a/lib/aprsme_web/live/user_forgot_password_live.ex +++ b/lib/aprsme_web/live/user_forgot_password_live.ex @@ -6,24 +6,56 @@ defmodule AprsmeWeb.UserForgotPasswordLive do def render(assigns) do ~H""" -
    - <.header class="text-center" /> +
    +
    +
    +
    +
    +

    Forgot your password?

    +

    We'll send a password reset link to your inbox

    -

    Forgot your password?

    -

    We'll send a password reset link to your inbox

    + <.simple_form + :let={_f} + id="reset_password_form" + for={%{}} + as={:user} + phx-submit="send_email" + > +
    + + +
    - <.simple_form :let={f} id="reset_password_form" for={%{}} as={:user} phx-submit="send_email"> - <.input field={{f, :email}} type="email" placeholder="Email" required /> - <:actions> - <.button phx-disable-with="Sending..." class="w-full"> - Send password reset instructions - - - -

    - <.link navigate={~p"/users/register"}>Register - | <.link navigate={~p"/users/log_in"}>Log in -

    +
    + +
    + + +
    OR
    + +
    + <.link navigate={~p"/users/register"} class="link link-primary"> + Register + + | + <.link navigate={~p"/users/log_in"} class="link link-primary"> + Log in + +
    +
    +
    +
    +
    """ end diff --git a/lib/aprsme_web/live/user_login_live.ex b/lib/aprsme_web/live/user_login_live.ex index 1c1a5e2..7b03837 100644 --- a/lib/aprsme_web/live/user_login_live.ex +++ b/lib/aprsme_web/live/user_login_live.ex @@ -7,41 +7,88 @@ defmodule AprsmeWeb.UserLoginLive do def render(assigns) do ~H""" -
    - <.header class="text-center" /> +
    +
    +
    +
    +
    +

    Sign in to account

    +

    + Don't have an account? + <.link navigate={~p"/users/register"} class="link link-primary"> + Sign up + + for an account now. +

    -

    Sign in to account

    -

    - Don't have an account? - <.link navigate={~p"/users/register"} class="font-semibold text-brand hover:underline"> - Sign up - - for an account now. -

    + <.simple_form + :let={f} + id="login_form" + for={@changeset} + action={~p"/users/log_in"} + as={:user} + phx-update="ignore" + > +
    + + +
    - <.simple_form - :let={f} - id="login_form" - for={@changeset} - action={~p"/users/log_in"} - as={:user} - phx-update="ignore" - > - <.input field={{f, :email}} type="email" label="Email" required /> - <.input field={{f, :password}} type="password" label="Password" required /> +
    + + +
    - <:actions :let={f}> - <.input field={{f, :remember_me}} type="checkbox" label="Keep me logged in" /> - <.link navigate={~p"/users/reset_password"} class="text-sm font-semibold"> - Forgot your password? - - - <:actions> - <.button phx-disable-with="Signing in..." class="w-full"> - Sign in - - - +
    + +
    + +
    + <.link navigate={~p"/users/reset_password"} class="link link-primary text-sm"> + Forgot your password? + +
    + +
    + +
    + +
    +
    +
    +
    """ end diff --git a/lib/aprsme_web/live/user_registration_live.ex b/lib/aprsme_web/live/user_registration_live.ex index 04da7d0..a2ff499 100644 --- a/lib/aprsme_web/live/user_registration_live.ex +++ b/lib/aprsme_web/live/user_registration_live.ex @@ -7,40 +7,77 @@ defmodule AprsmeWeb.UserRegistrationLive do def render(assigns) do ~H""" -
    - <.header class="text-center" /> +
    +
    +
    +
    +
    +

    Register for an account

    +

    + Already registered? + <.link navigate={~p"/users/log_in"} class="link link-primary"> + Sign in + + to your account now. +

    -

    Register for an account

    -

    - Already registered? - <.link navigate={~p"/users/log_in"} class="font-semibold text-brand hover:underline"> - Sign in - - to your account now. -

    + <.simple_form + :let={f} + id="registration_form" + for={@changeset} + phx-submit="save" + phx-change="validate" + phx-trigger-action={@trigger_submit} + action={~p"/users/log_in?_action=registered"} + method="post" + as={:user} + > +
    + Oops, something went wrong! Please check the errors below. +
    - <.simple_form - :let={f} - id="registration_form" - for={@changeset} - phx-submit="save" - phx-change="validate" - phx-trigger-action={@trigger_submit} - action={~p"/users/log_in?_action=registered"} - method="post" - as={:user} - > - <.error :if={@changeset.action == :insert}> - Oops, something went wrong! Please check the errors below. - +
    + + +
    - <.input field={{f, :email}} type="email" label="Email" required /> - <.input field={{f, :password}} type="password" label="Password" required /> +
    + + +
    - <:actions> - <.button phx-disable-with="Creating account..." class="w-full">Create an account - - +
    + +
    + +
    +
    +
    +
    """ end diff --git a/lib/aprsme_web/live/user_reset_password_live.ex b/lib/aprsme_web/live/user_reset_password_live.ex index 2d17479..cf82113 100644 --- a/lib/aprsme_web/live/user_reset_password_live.ex +++ b/lib/aprsme_web/live/user_reset_password_live.ex @@ -6,36 +6,75 @@ defmodule AprsmeWeb.UserResetPasswordLive do def render(assigns) do ~H""" -
    - <.header class="text-center" /> +
    +
    +
    +
    +
    +

    Reset Password

    +

    Enter your new password below

    - <.simple_form - :let={f} - for={@changeset} - id="reset_password_form" - phx-submit="reset_password" - phx-change="validate" - > - <.error :if={@changeset.action == :insert}> - Oops, something went wrong! Please check the errors below. - + <.simple_form + :let={f} + for={@changeset} + id="reset_password_form" + phx-submit="reset_password" + phx-change="validate" + > +
    + Oops, something went wrong! Please check the errors below. +
    - <.input field={{f, :password}} type="password" label="New password" required /> - <.input - field={{f, :password_confirmation}} - type="password" - label="Confirm new password" - required - /> - <:actions> - <.button phx-disable-with="Resetting..." class="w-full">Reset Password - - +
    + + +
    -

    - <.link navigate={~p"/users/register"}>Register - | <.link navigate={~p"/users/log_in"}>Log in -

    +
    + + +
    + +
    + +
    + + +
    OR
    + +
    + <.link navigate={~p"/users/register"} class="link link-primary"> + Register + + | + <.link navigate={~p"/users/log_in"} class="link link-primary"> + Log in + +
    +
    +
    +
    +
    """ end diff --git a/lib/aprsme_web/live/user_settings_live.ex b/lib/aprsme_web/live/user_settings_live.ex index 75590a9..c508aad 100644 --- a/lib/aprsme_web/live/user_settings_live.ex +++ b/lib/aprsme_web/live/user_settings_live.ex @@ -6,68 +6,147 @@ defmodule AprsmeWeb.UserSettingsLive do def render(assigns) do ~H""" - <.header /> +
    +
    +
    +

    Account Settings

    +

    Update your email address and password

    +
    - <.simple_form - :let={f} - id="email_form" - for={@email_changeset} - phx-submit="update_email" - phx-change="validate_email" - > - <.error :if={@email_changeset.action == :insert}> - Oops, something went wrong! Please check the errors below. - +
    + +
    +
    +

    Change Email

    - <.input field={{f, :email}} type="email" label="Email" required /> + <.simple_form + :let={f} + id="email_form" + for={@email_changeset} + phx-submit="update_email" + phx-change="validate_email" + > +
    + Oops, something went wrong! Please check the errors below. +
    - <.input - field={{f, :current_password}} - name="current_password" - id="current_password_for_email" - type="password" - label="Current password" - value={@email_form_current_password} - required - /> - <:actions> - <.button phx-disable-with="Changing...">Change Email - - +
    + + +
    - <.header /> +
    + + +
    - <.simple_form - :let={f} - id="password_form" - for={@password_changeset} - action={~p"/users/log_in?_action=password_updated"} - method="post" - phx-change="validate_password" - phx-submit="update_password" - phx-trigger-action={@trigger_submit} - > - <.error :if={@password_changeset.action == :insert}> - Oops, something went wrong! Please check the errors below. - +
    + +
    + +
    +
    + + +
    +
    +

    Change Password

    - <.input field={{f, :email}} type="hidden" value={@current_email} /> + <.simple_form + :let={f} + id="password_form" + for={@password_changeset} + action={~p"/users/log_in?_action=password_updated"} + method="post" + phx-change="validate_password" + phx-submit="update_password" + phx-trigger-action={@trigger_submit} + > +
    + Oops, something went wrong! Please check the errors below. +
    - <.input field={{f, :password}} type="password" label="New password" required /> - <.input field={{f, :password_confirmation}} type="password" label="Confirm new password" /> - <.input - field={{f, :current_password}} - name="current_password" - type="password" - label="Current password" - id="current_password_for_password" - value={@current_password} - required - /> - <:actions> - <.button phx-disable-with="Changing...">Change Password - - + + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + +
    + +
    +
    +
    +
    +
    """ end diff --git a/lib/aprsme_web/router.ex b/lib/aprsme_web/router.ex index 0b02588..4590d78 100644 --- a/lib/aprsme_web/router.ex +++ b/lib/aprsme_web/router.ex @@ -51,10 +51,37 @@ defmodule AprsmeWeb.Router do get "/status.json", PageController, :status_json end + ## Authentication routes + + scope "/", AprsmeWeb do + pipe_through [:browser, :redirect_if_user_is_authenticated] + + live_session :redirect_if_user_is_authenticated, + on_mount: [{AprsmeWeb.UserAuth, :redirect_if_user_is_authenticated}, {AprsmeWeb.LocaleHook, :set_locale}] do + live "/users/register", UserRegistrationLive, :new + live "/users/log_in", UserLoginLive, :new + live "/users/reset_password", UserForgotPasswordLive, :new + live "/users/reset_password/:token", UserResetPasswordLive, :edit + end + + post "/users/log_in", UserSessionController, :create + end + + scope "/", AprsmeWeb do + pipe_through [:browser, :require_authenticated_user] + + live_session :require_authenticated_user, + on_mount: [{AprsmeWeb.UserAuth, :ensure_authenticated}, {AprsmeWeb.LocaleHook, :set_locale}] do + live "/users/settings", UserSettingsLive, :edit + live "/users/settings/confirm_email/:token", UserSettingsLive, :confirm_email + end + end + scope "/", AprsmeWeb do pipe_through :browser - live_session :regular_pages, on_mount: [{AprsmeWeb.LocaleHook, :set_locale}] do + live_session :regular_pages, + on_mount: [{AprsmeWeb.UserAuth, :mount_current_user}, {AprsmeWeb.LocaleHook, :set_locale}] do live "/status", StatusLive.Index, :index live "/packets", PacketsLive.Index, :index live "/packets/:callsign", PacketsLive.CallsignView, :index @@ -89,30 +116,4 @@ defmodule AprsmeWeb.Router do forward "/mailbox", Plug.Swoosh.MailboxPreview end end - - ## Authentication routes - - scope "/", AprsmeWeb do - pipe_through [:browser, :redirect_if_user_is_authenticated] - - live_session :redirect_if_user_is_authenticated, - on_mount: [{AprsmeWeb.UserAuth, :redirect_if_user_is_authenticated}, {AprsmeWeb.LocaleHook, :set_locale}] do - live "/users/register", UserRegistrationLive, :new - live "/users/log_in", UserLoginLive, :new - live "/users/reset_password", UserForgotPasswordLive, :new - live "/users/reset_password/:token", UserResetPasswordLive, :edit - end - - post "/users/log_in", UserSessionController, :create - end - - scope "/", AprsmeWeb do - pipe_through [:browser, :require_authenticated_user] - - live_session :require_authenticated_user, - on_mount: [{AprsmeWeb.UserAuth, :ensure_authenticated}, {AprsmeWeb.LocaleHook, :set_locale}] do - live "/users/settings", UserSettingsLive, :edit - live "/users/settings/confirm_email/:token", UserSettingsLive, :confirm_email - end - end end