feat: refactor frontend routing and authentication
- Changed root div ID from 'root' to 'app' in index.html. - Updated package.json to include new dependencies for routing and state management. - Removed App component and replaced it with a router setup in main.tsx. - Added route definitions for login, about, and index pages. - Implemented authentication logic using Zustand for state management. - Created API client with Axios for handling requests and token management. - Added CORS support in the backend API. - Updated schema for login requests to use camelCase.
This commit is contained in:
55
libertas-frontend/src/routes/__root.tsx
Normal file
55
libertas-frontend/src/routes/__root.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import {
|
||||
Link,
|
||||
Outlet,
|
||||
createRootRouteWithContext,
|
||||
useNavigate,
|
||||
} from "@tanstack/react-router";
|
||||
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
||||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
||||
import type { QueryClient } from "@tanstack/react-query";
|
||||
import { useAuthStorage } from "@/hooks/use-auth-storage";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export const Route = createRootRouteWithContext<{
|
||||
queryClient: QueryClient;
|
||||
}>()({
|
||||
component: RootComponent,
|
||||
notFoundComponent: () => {
|
||||
return (
|
||||
<div>
|
||||
<p>This is the notFoundComponent configured on root route</p>
|
||||
<Link to="/">Start Over</Link>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
function RootComponent() {
|
||||
const token = useAuthStorage((s) => s.token);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (!token) {
|
||||
navigate({
|
||||
to: "/login",
|
||||
replace: true,
|
||||
});
|
||||
}
|
||||
}, [token, navigate]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex h-screen bg-gray-100">
|
||||
{/* <Sidebar /> */}
|
||||
<div className="flex-1 flex flex-col">
|
||||
{/* <Header /> */}
|
||||
<main className="flex-1 p-6 overflow-y-auto">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<ReactQueryDevtools buttonPosition="top-right" />
|
||||
<TanStackRouterDevtools position="bottom-right" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
14
libertas-frontend/src/routes/about.tsx
Normal file
14
libertas-frontend/src/routes/about.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import * as React from "react";
|
||||
|
||||
export const Route = createFileRoute("/about")({
|
||||
component: AboutComponent,
|
||||
});
|
||||
|
||||
function AboutComponent() {
|
||||
return (
|
||||
<div className="p-2">
|
||||
<h3>About</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
14
libertas-frontend/src/routes/index.tsx
Normal file
14
libertas-frontend/src/routes/index.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/")({
|
||||
component: Dashboard,
|
||||
});
|
||||
|
||||
function Dashboard() {
|
||||
return (
|
||||
<div className="p-4">
|
||||
<h1 className="text-2xl font-bold">Welcome to Libertas</h1>
|
||||
<p>This is your main dashboard.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
85
libertas-frontend/src/routes/login.tsx
Normal file
85
libertas-frontend/src/routes/login.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
||||
import { useLogin } from "@/features/auth/use-auth";
|
||||
import { useAuthStorage } from "@/hooks/use-auth-storage";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export const Route = createFileRoute("/login")({
|
||||
component: LoginPage,
|
||||
});
|
||||
|
||||
function LoginPage() {
|
||||
const navigate = useNavigate();
|
||||
const { mutate: login, isPending, error } = useLogin();
|
||||
const token = useAuthStorage((s) => s.token);
|
||||
|
||||
useEffect(() => {
|
||||
if (token) {
|
||||
navigate({ to: "/", replace: true });
|
||||
}
|
||||
}, [token, navigate]);
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const usernameOrEmail = formData.get("email") as string;
|
||||
const password = formData.get("password") as string;
|
||||
|
||||
login({ usernameOrEmail, password });
|
||||
};
|
||||
|
||||
console.log("LoginPage render, isPending:", isPending, "error:", error);
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-center h-screen bg-gray-100">
|
||||
<div className="w-full max-w-md p-8 space-y-6 bg-white rounded-lg shadow-md">
|
||||
<h1 className="text-2xl font-bold text-center">Login to Libertas</h1>
|
||||
<form className="space-y-6" onSubmit={handleSubmit}>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
Email or Username
|
||||
</label>
|
||||
<input
|
||||
id="email"
|
||||
name="email"
|
||||
type="text"
|
||||
required
|
||||
className="w-full px-3 py-2 mt-1 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="password"
|
||||
className="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
required
|
||||
className="w-full px-3 py-2 mt-1 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
{error && (
|
||||
<p className="text-sm text-red-600">
|
||||
Login failed. Please check your credentials.
|
||||
</p>
|
||||
)}
|
||||
<div>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isPending}
|
||||
className="w-full px-4 py-2 font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50"
|
||||
>
|
||||
{isPending ? "Logging in..." : "Login"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user