feat(password change): added in password link to email and change in lst

This commit is contained in:
2025-04-23 15:20:35 -05:00
parent 28b050859a
commit 86905b591b
6 changed files with 342 additions and 4 deletions

1
.gitignore vendored
View File

@@ -150,3 +150,4 @@ dist
.pnp.*
backend-0.1.3.zip
BulkForecastTemplate

View File

@@ -0,0 +1,193 @@
import { useForm } from "@tanstack/react-form";
import { useNavigate } from "@tanstack/react-router";
import axios from "axios";
import { useState } from "react";
import { toast } from "sonner";
import { LstCard } from "../extendedUI/LstCard";
import { CardContent, CardHeader } from "../ui/card";
import { Label } from "../ui/label";
import { Input } from "../ui/input";
import { Button } from "../ui/button";
export default function PasswordChange() {
const [saving, setSaving] = useState(false);
const token = localStorage.getItem("auth_token");
const navigate = useNavigate();
const form = useForm({
defaultValues: {
password: "",
confirmPassword: "",
},
onSubmit: async ({ value }) => {
setSaving(true);
try {
const res = await axios.patch("/api/auth/profile", value, {
headers: { Authorization: `Bearer ${token}` },
});
//console.log(res.data);
if (res.data.success) {
localStorage.removeItem("auth_token");
navigate({ to: "/login" });
form.reset();
toast.success(`Your password was just updated.`);
setSaving(false);
}
if (!res.data.success) {
toast.error(res.data.message);
setSaving(false);
}
} catch (error) {
console.log(error);
toast.error("There was an error updating your password");
setSaving(false);
}
},
});
return (
<div>
<LstCard className="w-96 ml-7">
<CardHeader>
<p>Password Change Form</p>
</CardHeader>
<CardContent>
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<form.Field
name="password"
// We can choose between form-wide and field-specific validators
validators={{
onChangeAsyncDebounceMs: 500,
onChangeAsync: ({ value }) => {
// if (
// window.location.pathname.includes(
// "/users"
// ) &&
// value.length === 0
// ) {
// return;
// }
if (value.length < 4) {
return "Password must be at least 4 characters long.";
}
if (!/[A-Z]/.test(value)) {
return "Password must contain at least one uppercase letter.";
}
if (!/[a-z]/.test(value)) {
return "Password must contain at least one lower case letter.";
}
if (!/[0-9]/.test(value)) {
return "Password must contain at least one number.";
}
if (
!/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(
value
)
) {
return "Password must contain at least one special character.";
}
},
}}
children={(field) => {
return (
<div className="m-2 min-w-48 max-w-96 p-2">
<Label
htmlFor="password1"
className="mb-2"
>
Password
</Label>
<Input
name={field.name}
value={field.state.value}
onBlur={field.handleBlur}
type="password"
onChange={(e) =>
field.handleChange(
e.target.value
)
}
/>
{field.state.meta.errors.length ? (
<em>
{field.state.meta.errors.join(
","
)}
</em>
) : null}
</div>
);
}}
/>
<form.Field
name="confirmPassword"
validators={{
onChange: ({ value, fieldApi }) => {
const password =
fieldApi.form.getFieldValue("password");
if (value !== password) {
return "Passwords do not match.";
}
},
}}
>
{(field) => {
return (
<div className="m-2 min-w-48 max-w-96 p-2">
<Label
htmlFor="confirmPassword"
className="mb-2"
>
Confirm Password
</Label>
<Input
name={field.name}
value={field.state.value}
onBlur={field.handleBlur}
type="password"
onChange={(e) =>
field.handleChange(
e.target.value
)
}
/>
{field.state.meta.errors.length ? (
<em>
{field.state.meta.errors.join(
", "
)}
</em>
) : null}
</div>
);
}}
</form.Field>
<div className="mt-4 ml-4 flex justify-end">
<Button
type="submit"
onClick={form.handleSubmit}
disabled={saving}
>
Save
</Button>
</div>
</form>
</CardContent>
</LstCard>
</div>
);
}

View File

@@ -0,0 +1,28 @@
import PasswordChange from "@/components/user/PasswordChange";
import { createFileRoute, redirect } from "@tanstack/react-router";
export const Route = createFileRoute("/(user)/passwordChange")({
component: RouteComponent,
beforeLoad: async () => {
const auth = localStorage.getItem("auth_token");
if (!auth) {
throw redirect({
to: "/login",
search: {
// Use the current location to power a redirect after login
// (Do not use `router.state.resolvedLocation` as it can
// potentially lag behind the actual current location)
redirect: location.pathname + location.search,
},
});
}
},
});
function RouteComponent() {
return (
<div>
<PasswordChange />
</div>
);
}

View File

@@ -80,8 +80,12 @@ export const Route = createRootRoute({
Hello {user?.username}
</DropdownMenuLabel>
<DropdownMenuSeparator />
{/* <DropdownMenuItem>Profile</DropdownMenuItem>
<DropdownMenuItem>Billing</DropdownMenuItem>
<DropdownMenuItem>
<Link to="/passwordChange">
Password Change
</Link>
</DropdownMenuItem>
{/* <DropdownMenuItem>Billing</DropdownMenuItem>
<DropdownMenuItem>Team</DropdownMenuItem>
<DropdownMenuItem>Subscription</DropdownMenuItem> */}
<hr className="solid"></hr>

View File

@@ -0,0 +1,106 @@
import RegisterForm from "@/components/auth/Register";
import { LstCard } from "@/components/extendedUI/LstCard";
import { CardContent, CardHeader } from "@/components/ui/card";
import { createFileRoute, redirect } from "@tanstack/react-router";
export const Route = createFileRoute("/register")({
component: RouteComponent,
beforeLoad: () => {
const isLoggedIn = localStorage.getItem("auth_token");
if (isLoggedIn) {
throw redirect({
to: "/",
});
}
},
});
function RouteComponent() {
return (
<div className="flex flex-row w-5/6 justify-between gap-2">
<RegisterForm />
<div>
<LstCard>
<CardHeader>
<h2 className="text-2xl font-bold mb-4">
Disclaimer and User Agreement
</h2>
</CardHeader>
<CardContent>
<div className="max-w-2xl mx-auto p-6 rounded-2xl shadow-md">
<ul className="list-decimal list-inside space-y-3">
<li>
<span className="font-bold">
Authentication Notice:
</span>
To interact with the Alpla prod through this
application, you must use your{" "}
<span className="font-semibold">
Windows login credentials
</span>
. These credentials are used solely for
authentication purposes.
</li>
{/* <li>
<span className="font-bold">
Password Privacy and Security:
</span>
This application{" "}
<span className="font-semibold">
does not store, sync, or transmit
</span>{" "}
your Windows password in any form.
Authentication is handled securely, and your
credentials are never logged or accessible
to the developers or third parties.
</li> */}
<li>
<span className="font-bold">
Data Handling:
</span>
All data accessed through the Alpla prod
remains the property of Alpla. The app
functions as a secure interface for
accessing and updating this information
where permitted by your role.
</li>
<li>
<span className="font-bold">
User Responsibility:
</span>
You are responsible for any actions
performed using your account. Please ensure
you do not share your credentials with
others and always log out of the application
when not in use.
</li>
<li>
<span className="font-bold">
No Warranty:
</span>
This application is provided "
<span className="italic">as is</span>"
without any warranty of any kind, either
expressed or implied. While every effort is
made to ensure functionality and data
accuracy, the app is subject to ongoing
development and may contain bugs or require
updates.
</li>
<li>
<span className="font-bold">
Consent Required:
</span>
By signing up or continuing to use this
application, you agree to the above terms
and acknowledge that you understand how your
login information is used.
</li>
</ul>
</div>
</CardContent>
</LstCard>
</div>
</div>
);
}

View File

@@ -22,11 +22,17 @@
Your password was change. Please find your new temporary password below:<br/><br/>
Temporary Password: <em><b>{{password}}</b></em><br/><br/>
Temporary Password: <em><b>{{password}}</b></em><br/><br/>
For security reasons, we strongly recommend changing your password as soon as possible.<br/><br/>
You can update it by logging into your account and navigating to the password settings section.<br/><br/>
You can update it by logging into your account, clicking your profile at the top right and click password change.<br/><br/>
Or <a href="http://{{server}}:{{port}}/passwordChange"
style="display:inline-block; padding:10px 20px; text-decoration:none; border-radius:5px;">
Click Here
</a> to login and change your password.<br/><br/>
Best regards,<br/><br/>
LST team<br/>