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:
2025-11-16 00:36:30 +01:00
parent f7a839b11a
commit 252491bd2f
19 changed files with 655 additions and 27 deletions

View 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" />
</>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}