{
return (
);
diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts
index b721a60..a81e988 100644
--- a/frontend/src/routeTree.gen.ts
+++ b/frontend/src/routeTree.gen.ts
@@ -15,6 +15,7 @@ import { Route as AdminLogsRouteImport } from './routes/admin/logs'
import { Route as authLoginRouteImport } from './routes/(auth)/login'
import { Route as authUserSignupRouteImport } from './routes/(auth)/user.signup'
import { Route as authUserResetpasswordRouteImport } from './routes/(auth)/user.resetpassword'
+import { Route as authUserProfileRouteImport } from './routes/(auth)/user.profile'
const AboutRoute = AboutRouteImport.update({
id: '/about',
@@ -46,12 +47,18 @@ const authUserResetpasswordRoute = authUserResetpasswordRouteImport.update({
path: '/user/resetpassword',
getParentRoute: () => rootRouteImport,
} as any)
+const authUserProfileRoute = authUserProfileRouteImport.update({
+ id: '/(auth)/user/profile',
+ path: '/user/profile',
+ getParentRoute: () => rootRouteImport,
+} as any)
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/about': typeof AboutRoute
'/login': typeof authLoginRoute
'/admin/logs': typeof AdminLogsRoute
+ '/user/profile': typeof authUserProfileRoute
'/user/resetpassword': typeof authUserResetpasswordRoute
'/user/signup': typeof authUserSignupRoute
}
@@ -60,6 +67,7 @@ export interface FileRoutesByTo {
'/about': typeof AboutRoute
'/login': typeof authLoginRoute
'/admin/logs': typeof AdminLogsRoute
+ '/user/profile': typeof authUserProfileRoute
'/user/resetpassword': typeof authUserResetpasswordRoute
'/user/signup': typeof authUserSignupRoute
}
@@ -69,6 +77,7 @@ export interface FileRoutesById {
'/about': typeof AboutRoute
'/(auth)/login': typeof authLoginRoute
'/admin/logs': typeof AdminLogsRoute
+ '/(auth)/user/profile': typeof authUserProfileRoute
'/(auth)/user/resetpassword': typeof authUserResetpasswordRoute
'/(auth)/user/signup': typeof authUserSignupRoute
}
@@ -79,6 +88,7 @@ export interface FileRouteTypes {
| '/about'
| '/login'
| '/admin/logs'
+ | '/user/profile'
| '/user/resetpassword'
| '/user/signup'
fileRoutesByTo: FileRoutesByTo
@@ -87,6 +97,7 @@ export interface FileRouteTypes {
| '/about'
| '/login'
| '/admin/logs'
+ | '/user/profile'
| '/user/resetpassword'
| '/user/signup'
id:
@@ -95,6 +106,7 @@ export interface FileRouteTypes {
| '/about'
| '/(auth)/login'
| '/admin/logs'
+ | '/(auth)/user/profile'
| '/(auth)/user/resetpassword'
| '/(auth)/user/signup'
fileRoutesById: FileRoutesById
@@ -104,6 +116,7 @@ export interface RootRouteChildren {
AboutRoute: typeof AboutRoute
authLoginRoute: typeof authLoginRoute
AdminLogsRoute: typeof AdminLogsRoute
+ authUserProfileRoute: typeof authUserProfileRoute
authUserResetpasswordRoute: typeof authUserResetpasswordRoute
authUserSignupRoute: typeof authUserSignupRoute
}
@@ -152,6 +165,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof authUserResetpasswordRouteImport
parentRoute: typeof rootRouteImport
}
+ '/(auth)/user/profile': {
+ id: '/(auth)/user/profile'
+ path: '/user/profile'
+ fullPath: '/user/profile'
+ preLoaderRoute: typeof authUserProfileRouteImport
+ parentRoute: typeof rootRouteImport
+ }
}
}
@@ -160,6 +180,7 @@ const rootRouteChildren: RootRouteChildren = {
AboutRoute: AboutRoute,
authLoginRoute: authLoginRoute,
AdminLogsRoute: AdminLogsRoute,
+ authUserProfileRoute: authUserProfileRoute,
authUserResetpasswordRoute: authUserResetpasswordRoute,
authUserSignupRoute: authUserSignupRoute,
}
diff --git a/frontend/src/routes/(auth)/-components/ChangePassword.tsx b/frontend/src/routes/(auth)/-components/ChangePassword.tsx
new file mode 100644
index 0000000..a691340
--- /dev/null
+++ b/frontend/src/routes/(auth)/-components/ChangePassword.tsx
@@ -0,0 +1,90 @@
+import { useRouter } from "@tanstack/react-router";
+import { toast } from "sonner";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { authClient } from "@/lib/auth-client";
+import { useAppForm } from "@/lib/formSutff";
+
+export default function ChangePassword() {
+ const router = useRouter();
+ const form = useAppForm({
+ defaultValues: {
+ currentPassword: "",
+ newPassword: "",
+ confirmPassword: "",
+ },
+ onSubmit: async ({ value }) => {
+ if (value.newPassword !== value.confirmPassword) {
+ toast.error("Passwords do not match");
+ return;
+ }
+ const { data, error } = await authClient.changePassword({
+ newPassword: value.newPassword,
+ currentPassword: value.currentPassword,
+ revokeOtherSessions: true,
+ });
+
+ if (data) {
+ toast.success("Password has been updated");
+ form.reset();
+ router.invalidate();
+
+ //navigate({ to: "/login" });
+ }
+
+ if (error) {
+ toast.success(error.message);
+ }
+ },
+ });
+ return (
+
+
+
+ Change password
+
+
+
+
+ {(field) => (
+
+ )}
+
+
+
+ {(field) => (
+
+ )}
+
+
+
+ {(field) => (
+
+ )}
+
+
+
+
+ Update Profile
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/routes/(auth)/-components/LoginForm.tsx b/frontend/src/routes/(auth)/-components/LoginForm.tsx
index fed4b9d..dbf3766 100644
--- a/frontend/src/routes/(auth)/-components/LoginForm.tsx
+++ b/frontend/src/routes/(auth)/-components/LoginForm.tsx
@@ -4,7 +4,6 @@ import {
Card,
CardContent,
CardDescription,
- CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
diff --git a/frontend/src/routes/(auth)/-components/ResetForm.tsx b/frontend/src/routes/(auth)/-components/ResetForm.tsx
new file mode 100644
index 0000000..12632a5
--- /dev/null
+++ b/frontend/src/routes/(auth)/-components/ResetForm.tsx
@@ -0,0 +1,76 @@
+import { Link } from "@tanstack/react-router";
+import { toast } from "sonner";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import { authClient } from "@/lib/auth-client";
+import { useAppForm } from "@/lib/formSutff";
+
+export default function ResetForm() {
+ const form = useAppForm({
+ defaultValues: {
+ email: "",
+ },
+ onSubmit: async ({ value }) => {
+ const { data, error } = await authClient.requestPasswordReset({
+ email: value.email,
+ redirectTo: `${window.location.origin}`,
+ });
+
+ if (data) {
+ toast.success(data.message);
+ }
+
+ if (error) {
+ toast.error(error.message);
+ }
+ },
+ });
+
+ return (
+
+
+
+ Reset your password
+
+ Enter your email address and we’ll send you a reset link
+
+
+
+
+ {(field) => (
+
+ )}
+
+
+
+
+ Send Reset Link
+
+
+
+
+
+ Remembered your password?{" "}
+
+ Back to login
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/routes/(auth)/-components/ResetPassword.tsx b/frontend/src/routes/(auth)/-components/ResetPassword.tsx
new file mode 100644
index 0000000..35f3cdb
--- /dev/null
+++ b/frontend/src/routes/(auth)/-components/ResetPassword.tsx
@@ -0,0 +1,94 @@
+import { Link } from "@tanstack/react-router";
+import React from "react";
+import { toast } from "sonner";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import { authClient } from "@/lib/auth-client";
+import { useAppForm } from "@/lib/formSutff";
+
+export default function ResetPassword({ token }: { token: string }) {
+ const form = useAppForm({
+ defaultValues: {
+ password: "",
+ confirmPassword: "",
+ },
+ onSubmit: async ({ value }) => {
+ if (!token) {
+ toast.error("No token was included");
+ return;
+ }
+
+ const { data, error } = await authClient.resetPassword({
+ newPassword: value.password,
+ token,
+ });
+
+ if (data) {
+ toast.success("Password has been reset");
+ }
+
+ if (error) {
+ toast.error(error.message);
+ }
+ },
+ });
+
+ return (
+
+
+
+ Reset your password
+
+ Enter your email address and we’ll send you a reset link
+
+
+
+
+ {(field) => (
+
+ )}
+
+
+ {(field) => (
+
+ )}
+
+
+
+
+ Send Reset Link
+
+
+
+
+
+ Remembered your password?{" "}
+
+ Back to login
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/routes/(auth)/user.profile.tsx b/frontend/src/routes/(auth)/user.profile.tsx
new file mode 100644
index 0000000..b5154ef
--- /dev/null
+++ b/frontend/src/routes/(auth)/user.profile.tsx
@@ -0,0 +1,99 @@
+import { createFileRoute, redirect, useRouter } from "@tanstack/react-router";
+import { toast } from "sonner";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import { authClient, useSession } from "@/lib/auth-client";
+import { useAppForm } from "@/lib/formSutff";
+import ChangePassword from "./-components/ChangePassword";
+
+export const Route = createFileRoute("/(auth)/user/profile")({
+ beforeLoad: async () => {
+ const result = await authClient.getSession({
+ query: { disableCookieCache: true }, // force DB/Server lookup
+ });
+
+ //console.log("session check:", result.data);
+
+ if (!result.data) {
+ throw redirect({
+ to: "/login",
+ search: {
+ redirect: location.pathname + location.search,
+ },
+ });
+ }
+ },
+ component: RouteComponent,
+});
+
+function RouteComponent() {
+ const { data: session } = useSession();
+ const router = useRouter();
+ const form = useAppForm({
+ defaultValues: {
+ name: session?.user.name,
+ },
+ onSubmit: async ({ value }) => {
+ const { data, error } = await authClient.updateUser({
+ name: value.name,
+ });
+
+ if (data) {
+ toast.success("Profile has been updated");
+ form.reset();
+ //navigate({ to: "/login" });
+ }
+
+ if (error) {
+ toast.success(error.message);
+ }
+ },
+ });
+ return (
+
+
+
+
+ Profile
+
+ Change your profile and password below
+
+
+
+
+
+ {(field) => (
+
+ )}
+
+
+
+
+ Update Profile
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/routes/(auth)/user.resetpassword.tsx b/frontend/src/routes/(auth)/user.resetpassword.tsx
index 14b6a40..c2534bf 100644
--- a/frontend/src/routes/(auth)/user.resetpassword.tsx
+++ b/frontend/src/routes/(auth)/user.resetpassword.tsx
@@ -1,9 +1,31 @@
-import { createFileRoute } from '@tanstack/react-router'
+import { createFileRoute, Link } from "@tanstack/react-router";
+import { toast } from "sonner";
+import z from "zod";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import { authClient } from "@/lib/auth-client";
+import { useAppForm } from "@/lib/formSutff";
+import ResetForm from "./-components/ResetForm";
+import ResetPassword from "./-components/ResetPassword";
-export const Route = createFileRoute('/(auth)/user/resetpassword')({
- component: RouteComponent,
-})
+export const Route = createFileRoute("/(auth)/user/resetpassword")({
+ validateSearch: z.object({
+ token: z.string().optional(),
+ }),
+ component: RouteComponent,
+});
function RouteComponent() {
- return
Hello "/(auth)/user/resetpassword"!
+ const { token } = Route.useSearch();
+
+ return (
+
+ {token ? : }
+
+ );
}
diff --git a/frontend/src/routes/(auth)/user.signup.tsx b/frontend/src/routes/(auth)/user.signup.tsx
index faeca71..fdb60fa 100644
--- a/frontend/src/routes/(auth)/user.signup.tsx
+++ b/frontend/src/routes/(auth)/user.signup.tsx
@@ -1,9 +1,128 @@
-import { createFileRoute } from '@tanstack/react-router'
+import { createFileRoute, Link, useNavigate } from "@tanstack/react-router";
+import { toast } from "sonner";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import { authClient } from "@/lib/auth-client";
+import { useAppForm } from "@/lib/formSutff";
-export const Route = createFileRoute('/(auth)/user/signup')({
- component: RouteComponent,
-})
+export const Route = createFileRoute("/(auth)/user/signup")({
+ component: RouteComponent,
+});
function RouteComponent() {
- return
Hello "/(auth)/user/signup"!
+ const navigate = useNavigate();
+ const form = useAppForm({
+ defaultValues: {
+ name: "",
+ email: "",
+ password: "",
+ confirmPassword: "",
+ },
+ onSubmit: async ({ value }) => {
+ if (value.password !== value.confirmPassword) {
+ toast.error("Passwords do not match");
+ return;
+ }
+
+ const { data, error } = await authClient.signUp.email({
+ name: value.name,
+ email: value.email,
+ password: value.password,
+ callbackURL: `${window.location.origin}/lst/app`,
+ });
+
+ if (data) {
+ toast.success(`Welcome ${value.name}, to lst.`);
+ navigate({ to: "/" });
+ }
+
+ if (error) {
+ toast.error(error.message);
+ }
+ },
+ });
+ return (
+
+
+
+ Create an account
+ Fill in your details to get started
+
+
+
+
+ {(field) => (
+
+ )}
+
+
+ {/* Email */}
+
+ {(field) => (
+
+ )}
+
+
+ {/* Password */}
+
+ {(field) => (
+
+ )}
+
+
+ {/* Confirm Password */}
+
+ {(field) => (
+
+ )}
+
+
+
+
+ Sign Up
+
+
+
+
+
+ Already have an account?{" "}
+
+ Log in
+
+
+
+
+
+ );
}
diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx
index 6e95d2b..ebea132 100644
--- a/frontend/src/routes/index.tsx
+++ b/frontend/src/routes/index.tsx
@@ -1,8 +1,8 @@
import { createFileRoute } from "@tanstack/react-router";
import z from "zod";
-
-import { useSession } from "../lib/auth-client";
+import { Button } from "@/components/ui/button";
+import { authClient, useSession } from "../lib/auth-client";
export const Route = createFileRoute("/")({
validateSearch: z.object({
@@ -13,11 +13,12 @@ export const Route = createFileRoute("/")({
});
function Index() {
- const { data: session, isPending } = useSession();
+ const { isPending } = useSession();
if (isPending)
return
Loading...
;
// if (!session) return
+
return (
Welcome Home!
diff --git a/tsconfig.json b/tsconfig.json
index cfe0069..a18c4ca 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -27,7 +27,8 @@
//"allowImportingTsExtensions": true,
"noEmit": false
},
- "include": ["backend/**/*", "types"],
+ "include": ["backend/**/*"
+],
"exclude": [
"node_modules",
"frontend",