add user auth

This commit is contained in:
Graham McIntire 2025-07-09 08:53:50 -05:00
parent fd82d6e7bd
commit 41eaec5c8e
No known key found for this signature in database
12 changed files with 627 additions and 221 deletions

View file

@ -119,6 +119,8 @@
color: oklch(58% 0.233 277.117) !important; color: oklch(58% 0.233 277.117) !important;
} }
/* Input field dark mode fixes */
/* Marker cluster styles */ /* Marker cluster styles */
.marker-cluster-small { .marker-cluster-small {
background-color: rgba(16, 185, 129, 0.8); background-color: rgba(16, 185, 129, 0.8);
@ -327,3 +329,59 @@
.historical-dot-marker { .historical-dot-marker {
z-index: 15 !important; 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;
}

View file

@ -237,7 +237,7 @@ defmodule AprsmeWeb.CoreComponents do
def simple_form(assigns) do def simple_form(assigns) do
~H""" ~H"""
<.form :let={f} for={@for} as={@as} {@rest}> <.form :let={f} for={@for} as={@as} {@rest}>
<div class="space-y-8 bg-white mt-10"> <div class="space-y-8 bg-base-100 mt-10">
{render_slot(@inner_block, f)} {render_slot(@inner_block, f)}
<div :for={action <- @actions} class="mt-2 flex items-center justify-between gap-6"> <div :for={action <- @actions} class="mt-2 flex items-center justify-between gap-6">
{render_slot(action, f)} {render_slot(action, f)}
@ -442,6 +442,7 @@ defmodule AprsmeWeb.CoreComponents do
""" """
attr :class, :string, default: "" attr :class, :string, default: ""
attr :dev_mode, :boolean, default: false attr :dev_mode, :boolean, default: false
attr :current_user, :any, default: nil
def header(assigns) do def header(assigns) do
~H""" ~H"""
@ -455,7 +456,7 @@ defmodule AprsmeWeb.CoreComponents do
</.link> </.link>
</div> </div>
<div class="navbar-center hidden lg:flex"> <div class="navbar-center hidden lg:flex">
<.navigation variant={:horizontal} /> <.navigation variant={:horizontal} current_user={@current_user} />
</div> </div>
<div class="navbar-end"> <div class="navbar-end">
<.theme_selector /> <.theme_selector />
@ -474,7 +475,7 @@ defmodule AprsmeWeb.CoreComponents do
tabindex="0" tabindex="0"
class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52" class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52"
> >
<.navigation variant={:vertical} /> <.navigation variant={:vertical} current_user={@current_user} />
</ul> </ul>
</div> </div>
</div> </div>
@ -750,6 +751,7 @@ defmodule AprsmeWeb.CoreComponents do
""" """
attr :class, :string, default: "" attr :class, :string, default: ""
attr :variant, :atom, values: [:horizontal, :vertical], default: :horizontal attr :variant, :atom, values: [:horizontal, :vertical], default: :horizontal
attr :current_user, :any, default: nil
def navigation(assigns) do def navigation(assigns) do
~H""" ~H"""
@ -764,6 +766,29 @@ defmodule AprsmeWeb.CoreComponents do
{gettext("About")} {gettext("About")}
</.link> </.link>
</li> </li>
<%= if @current_user do %>
<li>
<.link navigate="/users/settings" class="text-gray-900 hover:text-gray-700">
{gettext("Settings")}
</.link>
</li>
<li>
<.link href="/users/log_out" method="delete" class="text-gray-900 hover:text-gray-700">
{gettext("Log out")}
</.link>
</li>
<% else %>
<li>
<.link navigate="/users/register" class="text-gray-900 hover:text-gray-700">
{gettext("Register")}
</.link>
</li>
<li>
<.link navigate="/users/log_in" class="text-gray-900 hover:text-gray-700">
{gettext("Log in")}
</.link>
</li>
<% end %>
</ul> </ul>
<% else %> <% else %>
<li><.link navigate="/" class="text-gray-900 hover:text-gray-700">{gettext("Home")}</.link></li> <li><.link navigate="/" class="text-gray-900 hover:text-gray-700">{gettext("Home")}</.link></li>
@ -771,6 +796,29 @@ defmodule AprsmeWeb.CoreComponents do
<li> <li>
<.link navigate="/about" class="text-gray-900 hover:text-gray-700">{gettext("About")}</.link> <.link navigate="/about" class="text-gray-900 hover:text-gray-700">{gettext("About")}</.link>
</li> </li>
<%= if @current_user do %>
<li>
<.link navigate="/users/settings" class="text-gray-900 hover:text-gray-700">
{gettext("Settings")}
</.link>
</li>
<li>
<.link href="/users/log_out" method="delete" class="text-gray-900 hover:text-gray-700">
{gettext("Log out")}
</.link>
</li>
<% else %>
<li>
<.link navigate="/users/register" class="text-gray-900 hover:text-gray-700">
{gettext("Register")}
</.link>
</li>
<li>
<.link navigate="/users/log_in" class="text-gray-900 hover:text-gray-700">
{gettext("Log in")}
</.link>
</li>
<% end %>
<% end %> <% end %>
""" """
end end

View file

@ -200,7 +200,7 @@
} }
</style> </style>
<.header dev_mode={Application.get_env(:aprsme, :dev_routes, false)} /> <.header dev_mode={Application.get_env(:aprsme, :dev_routes, false)} current_user={@current_user} />
<main <main
id="main-content" id="main-content"
class={if assigns[:map_page], do: "min-h-screen bg-base-100", else: "bg-base-100"} class={if assigns[:map_page], do: "min-h-screen bg-base-100", else: "bg-base-100"}

View file

@ -906,7 +906,7 @@ defmodule AprsmeWeb.MapLive.Index do
</svg> </svg>
<span class="font-medium">{gettext("Navigation")}</span> <span class="font-medium">{gettext("Navigation")}</span>
</div> </div>
<.navigation variant={:vertical} class="text-sm" /> <.navigation variant={:vertical} class="text-sm" current_user={@current_user} />
</div> </div>
<!-- Deployment Information --> <!-- Deployment Information -->

View file

@ -6,25 +6,61 @@ defmodule AprsmeWeb.UserConfirmationInstructionsLive do
def render(assigns) do def render(assigns) do
~H""" ~H"""
<.header /> <div class="hero min-h-screen bg-base-200">
<div class="hero-content text-center">
<div class="max-w-md">
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h1 class="card-title text-3xl mb-4 justify-center">
Resend confirmation instructions
</h1>
<p class="text-base-content/70 mb-6">
Enter your email address to receive a new confirmation link
</p>
<.simple_form <.simple_form
:let={f} :let={_f}
for={%{}} for={%{}}
as={:user} as={:user}
id="resend_confirmation_form" id="resend_confirmation_form"
phx-submit="send_instructions" phx-submit="send_instructions"
> >
<.input field={{f, :email}} type="email" label="Email" required /> <div class="form-control w-full">
<:actions> <label class="label">
<.button phx-disable-with="Sending...">Resend confirmation instructions</.button> <span class="label-text">Email</span>
</:actions> </label>
</.simple_form> <input
type="email"
name="user[email]"
class="input input-bordered w-full bg-base-100 text-base-content"
placeholder="Enter your email"
required
/>
</div>
<p> <div class="form-control mt-6">
<.link navigate={~p"/users/register"}>Register</.link> <button type="submit" class="btn btn-primary w-full" phx-disable-with="Sending...">
| <.link navigate={~p"/users/log_in"}>Log in</.link> Resend confirmation instructions
</p> </button>
</div>
</.simple_form>
<div class="divider">OR</div>
<div class="text-center text-sm">
<.link navigate={~p"/users/register"} class="link link-primary">
Register
</.link>
|
<.link navigate={~p"/users/log_in"} class="link link-primary">
Log in
</.link>
</div>
</div>
</div>
</div>
</div>
</div>
""" """
end end

View file

@ -6,20 +6,49 @@ defmodule AprsmeWeb.UserConfirmationLive do
def render(%{live_action: :edit} = assigns) do def render(%{live_action: :edit} = assigns) do
~H""" ~H"""
<div class="mx-auto max-w-sm"> <div class="hero min-h-screen bg-base-200">
<.header class="text-center" /> <div class="hero-content text-center">
<div class="max-w-md">
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h1 class="card-title text-3xl mb-4 justify-center">Confirm your account</h1>
<p class="text-base-content/70 mb-6">Click the button below to confirm your account</p>
<.simple_form :let={f} for={%{}} as={:user} id="confirmation_form" phx-submit="confirm_account"> <.simple_form
<.input field={{f, :token}} type="hidden" value={@token} /> :let={_f}
<:actions> for={%{}}
<.button phx-disable-with="Confirming..." class="w-full">Confirm my account</.button> as={:user}
</:actions> id="confirmation_form"
</.simple_form> phx-submit="confirm_account"
>
<input type="hidden" name="user[token]" value={@token} />
<p class="text-center mt-4"> <div class="form-control mt-6">
<.link navigate={~p"/users/register"}>Register</.link> <button
| <.link navigate={~p"/users/log_in"}>Log in</.link> type="submit"
</p> class="btn btn-primary w-full"
phx-disable-with="Confirming..."
>
Confirm my account
</button>
</div>
</.simple_form>
<div class="divider">OR</div>
<div class="text-center text-sm">
<.link navigate={~p"/users/register"} class="link link-primary">
Register
</.link>
|
<.link navigate={~p"/users/log_in"} class="link link-primary">
Log in
</.link>
</div>
</div>
</div>
</div>
</div>
</div> </div>
""" """
end end

View file

@ -6,24 +6,56 @@ defmodule AprsmeWeb.UserForgotPasswordLive do
def render(assigns) do def render(assigns) do
~H""" ~H"""
<div class="mx-auto max-w-sm"> <div class="hero min-h-screen bg-base-200">
<.header class="text-center" /> <div class="hero-content text-center">
<div class="max-w-md">
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h1 class="card-title text-3xl mb-4 justify-center">Forgot your password?</h1>
<p class="text-base-content/70 mb-6">We'll send a password reset link to your inbox</p>
<h2 class="text-center text-2xl font-bold mt-6 mb-2">Forgot your password?</h2> <.simple_form
<p class="text-center text-gray-600 mb-6">We'll send a password reset link to your inbox</p> :let={_f}
id="reset_password_form"
for={%{}}
as={:user}
phx-submit="send_email"
>
<div class="form-control w-full">
<label class="label">
<span class="label-text">Email</span>
</label>
<input
type="email"
name="user[email]"
class="input input-bordered w-full bg-base-100 text-base-content"
placeholder="Enter your email"
required
/>
</div>
<.simple_form :let={f} id="reset_password_form" for={%{}} as={:user} phx-submit="send_email"> <div class="form-control mt-6">
<.input field={{f, :email}} type="email" placeholder="Email" required /> <button type="submit" class="btn btn-primary w-full" phx-disable-with="Sending...">
<:actions> Send password reset instructions
<.button phx-disable-with="Sending..." class="w-full"> </button>
Send password reset instructions </div>
</.button> </.simple_form>
</:actions>
</.simple_form> <div class="divider">OR</div>
<p class="text-center mt-4">
<.link navigate={~p"/users/register"}>Register</.link> <div class="text-center text-sm">
| <.link navigate={~p"/users/log_in"}>Log in</.link> <.link navigate={~p"/users/register"} class="link link-primary">
</p> Register
</.link>
|
<.link navigate={~p"/users/log_in"} class="link link-primary">
Log in
</.link>
</div>
</div>
</div>
</div>
</div>
</div> </div>
""" """
end end

View file

@ -7,41 +7,88 @@ defmodule AprsmeWeb.UserLoginLive do
def render(assigns) do def render(assigns) do
~H""" ~H"""
<div class="mx-auto max-w-sm"> <div class="hero min-h-screen bg-base-200">
<.header class="text-center" /> <div class="hero-content text-center">
<div class="max-w-md">
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h1 class="card-title text-3xl mb-4 justify-center">Sign in to account</h1>
<p class="text-base-content/70 mb-6">
Don't have an account?
<.link navigate={~p"/users/register"} class="link link-primary">
Sign up
</.link>
for an account now.
</p>
<h2 class="text-center text-2xl font-bold mt-6 mb-2">Sign in to account</h2> <.simple_form
<p class="text-center text-gray-600 mb-6"> :let={f}
Don't have an account? id="login_form"
<.link navigate={~p"/users/register"} class="font-semibold text-brand hover:underline"> for={@changeset}
Sign up action={~p"/users/log_in"}
</.link> as={:user}
for an account now. phx-update="ignore"
</p> >
<div class="form-control w-full">
<label class="label">
<span class="label-text">Email</span>
</label>
<input
type="email"
name={Phoenix.HTML.Form.input_name(f, :email)}
value={Phoenix.HTML.Form.input_value(f, :email) || ""}
class="input input-bordered w-full"
placeholder="Enter your email"
required
/>
</div>
<.simple_form <div class="form-control w-full">
:let={f} <label class="label">
id="login_form" <span class="label-text">Password</span>
for={@changeset} </label>
action={~p"/users/log_in"} <input
as={:user} type="password"
phx-update="ignore" name={Phoenix.HTML.Form.input_name(f, :password)}
> value={Phoenix.HTML.Form.input_value(f, :password) || ""}
<.input field={{f, :email}} type="email" label="Email" required /> class="input input-bordered w-full"
<.input field={{f, :password}} type="password" label="Password" required /> placeholder="Enter your password"
required
/>
</div>
<:actions :let={f}> <div class="form-control">
<.input field={{f, :remember_me}} type="checkbox" label="Keep me logged in" /> <label class="label cursor-pointer justify-start">
<.link navigate={~p"/users/reset_password"} class="text-sm font-semibold"> <input
Forgot your password? type="checkbox"
</.link> name={Phoenix.HTML.Form.input_name(f, :remember_me)}
</:actions> value="true"
<:actions> class="checkbox checkbox-primary mr-2"
<.button phx-disable-with="Signing in..." class="w-full"> />
Sign in <span aria-hidden="true"></span> <span class="label-text">Keep me logged in</span>
</.button> </label>
</:actions> </div>
</.simple_form>
<div class="text-center mb-4">
<.link navigate={~p"/users/reset_password"} class="link link-primary text-sm">
Forgot your password?
</.link>
</div>
<div class="form-control mt-6">
<button
type="submit"
class="btn btn-primary w-full"
phx-disable-with="Signing in..."
>
Sign in
</button>
</div>
</.simple_form>
</div>
</div>
</div>
</div>
</div> </div>
""" """
end end

View file

@ -7,40 +7,77 @@ defmodule AprsmeWeb.UserRegistrationLive do
def render(assigns) do def render(assigns) do
~H""" ~H"""
<div class="mx-auto max-w-sm"> <div class="hero min-h-screen bg-base-200">
<.header class="text-center" /> <div class="hero-content text-center">
<div class="max-w-md">
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h1 class="card-title text-3xl mb-4 justify-center">Register for an account</h1>
<p class="text-base-content/70 mb-6">
Already registered?
<.link navigate={~p"/users/log_in"} class="link link-primary">
Sign in
</.link>
to your account now.
</p>
<h2 class="text-center text-2xl font-bold mt-6 mb-2">Register for an account</h2> <.simple_form
<p class="text-center text-gray-600 mb-6"> :let={f}
Already registered? id="registration_form"
<.link navigate={~p"/users/log_in"} class="font-semibold text-brand hover:underline"> for={@changeset}
Sign in phx-submit="save"
</.link> phx-change="validate"
to your account now. phx-trigger-action={@trigger_submit}
</p> action={~p"/users/log_in?_action=registered"}
method="post"
as={:user}
>
<div :if={@changeset.action == :insert} class="alert alert-error mb-4">
<span>Oops, something went wrong! Please check the errors below.</span>
</div>
<.simple_form <div class="form-control w-full">
:let={f} <label class="label">
id="registration_form" <span class="label-text">Email</span>
for={@changeset} </label>
phx-submit="save" <input
phx-change="validate" type="email"
phx-trigger-action={@trigger_submit} name={Phoenix.HTML.Form.input_name(f, :email)}
action={~p"/users/log_in?_action=registered"} value={Phoenix.HTML.Form.input_value(f, :email) || ""}
method="post" class="input input-bordered w-full bg-base-100 text-base-content"
as={:user} placeholder="Enter your email"
> required
<.error :if={@changeset.action == :insert}> />
Oops, something went wrong! Please check the errors below. </div>
</.error>
<.input field={{f, :email}} type="email" label="Email" required /> <div class="form-control w-full">
<.input field={{f, :password}} type="password" label="Password" required /> <label class="label">
<span class="label-text">Password</span>
</label>
<input
type="password"
name={Phoenix.HTML.Form.input_name(f, :password)}
value={Phoenix.HTML.Form.input_value(f, :password) || ""}
class="input input-bordered w-full bg-base-100 text-base-content"
placeholder="Enter your password"
required
/>
</div>
<:actions> <div class="form-control mt-6">
<.button phx-disable-with="Creating account..." class="w-full">Create an account</.button> <button
</:actions> type="submit"
</.simple_form> class="btn btn-primary w-full"
phx-disable-with="Creating account..."
>
Create an account
</button>
</div>
</.simple_form>
</div>
</div>
</div>
</div>
</div> </div>
""" """
end end

View file

@ -6,36 +6,75 @@ defmodule AprsmeWeb.UserResetPasswordLive do
def render(assigns) do def render(assigns) do
~H""" ~H"""
<div class="mx-auto max-w-sm"> <div class="hero min-h-screen bg-base-200">
<.header class="text-center" /> <div class="hero-content text-center">
<div class="max-w-md">
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h1 class="card-title text-3xl mb-4 justify-center">Reset Password</h1>
<p class="text-base-content/70 mb-6">Enter your new password below</p>
<.simple_form <.simple_form
:let={f} :let={f}
for={@changeset} for={@changeset}
id="reset_password_form" id="reset_password_form"
phx-submit="reset_password" phx-submit="reset_password"
phx-change="validate" phx-change="validate"
> >
<.error :if={@changeset.action == :insert}> <div :if={@changeset.action == :insert} class="alert alert-error mb-4">
Oops, something went wrong! Please check the errors below. <span>Oops, something went wrong! Please check the errors below.</span>
</.error> </div>
<.input field={{f, :password}} type="password" label="New password" required /> <div class="form-control w-full">
<.input <label class="label">
field={{f, :password_confirmation}} <span class="label-text">New password</span>
type="password" </label>
label="Confirm new password" <input
required type="password"
/> name={Phoenix.HTML.Form.input_name(f, :password)}
<:actions> value={Phoenix.HTML.Form.input_value(f, :password) || ""}
<.button phx-disable-with="Resetting..." class="w-full">Reset Password</.button> class="input input-bordered w-full bg-base-100 text-base-content"
</:actions> placeholder="Enter new password"
</.simple_form> required
/>
</div>
<p class="text-center mt-4"> <div class="form-control w-full">
<.link navigate={~p"/users/register"}>Register</.link> <label class="label">
| <.link navigate={~p"/users/log_in"}>Log in</.link> <span class="label-text">Confirm new password</span>
</p> </label>
<input
type="password"
name={Phoenix.HTML.Form.input_name(f, :password_confirmation)}
value={Phoenix.HTML.Form.input_value(f, :password_confirmation) || ""}
class="input input-bordered w-full bg-base-100 text-base-content"
placeholder="Confirm new password"
required
/>
</div>
<div class="form-control mt-6">
<button type="submit" class="btn btn-primary w-full" phx-disable-with="Resetting...">
Reset Password
</button>
</div>
</.simple_form>
<div class="divider">OR</div>
<div class="text-center text-sm">
<.link navigate={~p"/users/register"} class="link link-primary">
Register
</.link>
|
<.link navigate={~p"/users/log_in"} class="link link-primary">
Log in
</.link>
</div>
</div>
</div>
</div>
</div>
</div> </div>
""" """
end end

View file

@ -6,68 +6,147 @@ defmodule AprsmeWeb.UserSettingsLive do
def render(assigns) do def render(assigns) do
~H""" ~H"""
<.header /> <div class="hero min-h-screen bg-base-200">
<div class="hero-content flex-col w-full max-w-4xl">
<div class="text-center mb-8">
<h1 class="text-4xl font-bold">Account Settings</h1>
<p class="text-base-content/70">Update your email address and password</p>
</div>
<.simple_form <div class="grid grid-cols-1 md:grid-cols-2 gap-8 w-full">
:let={f} <!-- Change Email Section -->
id="email_form" <div class="card bg-base-100 shadow-xl">
for={@email_changeset} <div class="card-body">
phx-submit="update_email" <h2 class="card-title text-2xl mb-4">Change Email</h2>
phx-change="validate_email"
>
<.error :if={@email_changeset.action == :insert}>
Oops, something went wrong! Please check the errors below.
</.error>
<.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"
>
<div :if={@email_changeset.action == :insert} class="alert alert-error mb-4">
<span>Oops, something went wrong! Please check the errors below.</span>
</div>
<.input <div class="form-control w-full">
field={{f, :current_password}} <label class="label">
name="current_password" <span class="label-text">Email</span>
id="current_password_for_email" </label>
type="password" <input
label="Current password" type="email"
value={@email_form_current_password} name={Phoenix.HTML.Form.input_name(f, :email)}
required value={Phoenix.HTML.Form.input_value(f, :email) || ""}
/> class="input input-bordered w-full bg-base-100 text-base-content"
<:actions> placeholder="Enter your email"
<.button phx-disable-with="Changing...">Change Email</.button> required
</:actions> />
</.simple_form> </div>
<.header /> <div class="form-control w-full">
<label class="label">
<span class="label-text">Current password</span>
</label>
<input
type="password"
name="current_password"
id="current_password_for_email"
value={@email_form_current_password || ""}
class="input input-bordered w-full bg-base-100 text-base-content"
placeholder="Enter current password"
required
/>
</div>
<.simple_form <div class="form-control mt-6">
:let={f} <button type="submit" class="btn btn-primary w-full" phx-disable-with="Changing...">
id="password_form" Change Email
for={@password_changeset} </button>
action={~p"/users/log_in?_action=password_updated"} </div>
method="post" </.simple_form>
phx-change="validate_password" </div>
phx-submit="update_password" </div>
phx-trigger-action={@trigger_submit}
>
<.error :if={@password_changeset.action == :insert}>
Oops, something went wrong! Please check the errors below.
</.error>
<.input field={{f, :email}} type="hidden" value={@current_email} /> <!-- Change Password Section -->
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title text-2xl mb-4">Change Password</h2>
<.input field={{f, :password}} type="password" label="New password" required /> <.simple_form
<.input field={{f, :password_confirmation}} type="password" label="Confirm new password" /> :let={f}
<.input id="password_form"
field={{f, :current_password}} for={@password_changeset}
name="current_password" action={~p"/users/log_in?_action=password_updated"}
type="password" method="post"
label="Current password" phx-change="validate_password"
id="current_password_for_password" phx-submit="update_password"
value={@current_password} phx-trigger-action={@trigger_submit}
required >
/> <div :if={@password_changeset.action == :insert} class="alert alert-error mb-4">
<:actions> <span>Oops, something went wrong! Please check the errors below.</span>
<.button phx-disable-with="Changing...">Change Password</.button> </div>
</:actions>
</.simple_form> <input
type="hidden"
name={Phoenix.HTML.Form.input_name(f, :email)}
value={@current_email}
/>
<div class="form-control w-full">
<label class="label">
<span class="label-text">New password</span>
</label>
<input
type="password"
name={Phoenix.HTML.Form.input_name(f, :password)}
value={Phoenix.HTML.Form.input_value(f, :password) || ""}
class="input input-bordered w-full bg-base-100 text-base-content"
placeholder="Enter new password"
required
/>
</div>
<div class="form-control w-full">
<label class="label">
<span class="label-text">Confirm new password</span>
</label>
<input
type="password"
name={Phoenix.HTML.Form.input_name(f, :password_confirmation)}
value={Phoenix.HTML.Form.input_value(f, :password_confirmation) || ""}
class="input input-bordered w-full bg-base-100 text-base-content"
placeholder="Confirm new password"
required
/>
</div>
<div class="form-control w-full">
<label class="label">
<span class="label-text">Current password</span>
</label>
<input
type="password"
name="current_password"
id="current_password_for_password"
value={@current_password || ""}
class="input input-bordered w-full bg-base-100 text-base-content"
placeholder="Enter current password"
required
/>
</div>
<div class="form-control mt-6">
<button type="submit" class="btn btn-primary w-full" phx-disable-with="Changing...">
Change Password
</button>
</div>
</.simple_form>
</div>
</div>
</div>
</div>
</div>
""" """
end end

View file

@ -51,10 +51,37 @@ defmodule AprsmeWeb.Router do
get "/status.json", PageController, :status_json get "/status.json", PageController, :status_json
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
scope "/", AprsmeWeb do scope "/", AprsmeWeb do
pipe_through :browser 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 "/status", StatusLive.Index, :index
live "/packets", PacketsLive.Index, :index live "/packets", PacketsLive.Index, :index
live "/packets/:callsign", PacketsLive.CallsignView, :index live "/packets/:callsign", PacketsLive.CallsignView, :index
@ -89,30 +116,4 @@ defmodule AprsmeWeb.Router do
forward "/mailbox", Plug.Swoosh.MailboxPreview forward "/mailbox", Plug.Swoosh.MailboxPreview
end end
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 end