diff --git a/.gitignore b/.gitignore index e1d34a3..f97cf83 100644 --- a/.gitignore +++ b/.gitignore @@ -150,3 +150,4 @@ dist .pnp.* backend-0.1.3.zip +BulkForecastTemplate diff --git a/frontend/src/components/user/PasswordChange.tsx b/frontend/src/components/user/PasswordChange.tsx new file mode 100644 index 0000000..f286363 --- /dev/null +++ b/frontend/src/components/user/PasswordChange.tsx @@ -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 ( +
+ + +

Password Change Form

+
+ +
{ + e.preventDefault(); + e.stopPropagation(); + }} + > + { + // 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 ( +
+ + + field.handleChange( + e.target.value + ) + } + /> + {field.state.meta.errors.length ? ( + + {field.state.meta.errors.join( + "," + )} + + ) : null} +
+ ); + }} + /> + + { + const password = + fieldApi.form.getFieldValue("password"); + if (value !== password) { + return "Passwords do not match."; + } + }, + }} + > + {(field) => { + return ( +
+ + + field.handleChange( + e.target.value + ) + } + /> + {field.state.meta.errors.length ? ( + + {field.state.meta.errors.join( + ", " + )} + + ) : null} +
+ ); + }} +
+
+ +
+ +
+
+
+ ); +} diff --git a/frontend/src/routes/(user)/passwordChange.tsx b/frontend/src/routes/(user)/passwordChange.tsx new file mode 100644 index 0000000..be396c8 --- /dev/null +++ b/frontend/src/routes/(user)/passwordChange.tsx @@ -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 ( +
+ +
+ ); +} diff --git a/frontend/src/routes/__root.tsx b/frontend/src/routes/__root.tsx index 8b4eb68..1311a4c 100644 --- a/frontend/src/routes/__root.tsx +++ b/frontend/src/routes/__root.tsx @@ -80,8 +80,12 @@ export const Route = createRootRoute({ Hello {user?.username} - {/* Profile - Billing + + + Password Change + + + {/* Billing Team Subscription */}
diff --git a/frontend/src/routes/register.tsx b/frontend/src/routes/register.tsx new file mode 100644 index 0000000..536541c --- /dev/null +++ b/frontend/src/routes/register.tsx @@ -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 ( +
+ +
+ + +

+ Disclaimer and User Agreement +

+
+ +
+
    +
  • + + Authentication Notice: + + To interact with the Alpla prod through this + application, you must use your{" "} + + Windows login credentials + + . These credentials are used solely for + authentication purposes. +
  • + {/*
  • + + Password Privacy and Security: + + This application{" "} + + does not store, sync, or transmit + {" "} + your Windows password in any form. + Authentication is handled securely, and your + credentials are never logged or accessible + to the developers or third parties. +
  • */} +
  • + + Data Handling: + + 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. +
  • +
  • + + User Responsibility: + + 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. +
  • +
  • + + No Warranty: + + This application is provided " + as is" + 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. +
  • +
  • + + Consent Required: + + 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. +
  • +
+
+
+
+
+
+ ); +} diff --git a/server/services/notifications/utils/views/passwordReset.hbs b/server/services/notifications/utils/views/passwordReset.hbs index db7789d..26d8f9b 100644 --- a/server/services/notifications/utils/views/passwordReset.hbs +++ b/server/services/notifications/utils/views/passwordReset.hbs @@ -22,11 +22,17 @@ Your password was change. Please find your new temporary password below:

- Temporary Password: {{password}}

+ Temporary Password: {{password}}

For security reasons, we strongly recommend changing your password as soon as possible.

- You can update it by logging into your account and navigating to the password settings section.

+ You can update it by logging into your account, clicking your profile at the top right and click password change.

+ + + Or + Click Here + to login and change your password.

Best regards,

LST team