Compare commits

...

2 Commits

Author SHA1 Message Date
5f8943492e feat(lst): tan stack routes added with protected routes 2025-02-19 20:11:40 -06:00
83f6fbf760 chore(release): 1.1.0 2025-02-19 14:08:11 -06:00
24 changed files with 626 additions and 75 deletions

View File

@@ -1,16 +1,16 @@
{
"types": [
{ "type": "feat", "section": "🌟 Enhancements" },
{ "type": "fix", "section": "🐛 Bug fixes" },
{ "type": "chore", "hidden": false, "section": "📝 Chore" },
{ "type": "docs", "section": "📚 Documentation" },
{ "type": "style", "hidden": true },
{ "type": "refactor", "section": "🛠️ Code Refactor" },
{ "type": "perf", "hidden": false, "section": "🚀 Code Refactor" },
{ "type": "test", "section": "📝 Testing Code" },
{ "type": "ci", "section": "📈 Project changes" }
],
"commitUrlFormat": "https://git.tuffraid.net/cowch/Lst_Backend/commits{{hash}}",
"compareUrlFormat": "https://git.tuffraid.net/cowch/Lst_Backend/compare/{{previousTag}}...{{currentTag}}",
"header": "# All notable changes to the LST project will be documented in this file.\n`"
"types": [
{"type": "feat", "section": "🌟 Enhancements"},
{"type": "fix", "section": "🐛 Bug fixes"},
{"type": "chore", "hidden": false, "section": "📝 Chore"},
{"type": "docs", "section": "📚 Documentation"},
{"type": "style", "hidden": true},
{"type": "refactor", "section": "🛠️ Code Refactor"},
{"type": "perf", "hidden": false, "section": "🚀 Code Refactor"},
{"type": "test", "section": "📝 Testing Code"},
{"type": "ci", "section": "📈 Project changes"}
],
"commitUrlFormat": "https://git.tuffraid.net/cowch/lstV2/commits{{hash}}",
"compareUrlFormat": "https://git.tuffraid.net/cowch/lstV2/compare/{{previousTag}}...{{currentTag}}",
"header": "# All CHanges to LST can be found below.\n`"
}

17
CHANGELOG.md Normal file
View File

@@ -0,0 +1,17 @@
# All notable changes to the LST project will be documented in this file.
`
## 1.1.0 (2025-02-19)
### 🌟 Enhancements
* **apps\frontend:** added in tailwind ([aa2ba8d](https://git.tuffraid.net/cowch/Lst_Backend/commitsaa2ba8d79a896f856204fb03f9ca64b4848edddc))
* **apps\server:** auth stuff ([ca27264](https://git.tuffraid.net/cowch/Lst_Backend/commitsca27264bb006c0b1de085fd06e68d4ea388ad82a))
* **lst:** added in basic authentication ([5f7a3dd](https://git.tuffraid.net/cowch/Lst_Backend/commits5f7a3dd182f87f964813faaa169206842c3b6772))
* **lst:** intial scafolding for the new system ([2e02536](https://git.tuffraid.net/cowch/Lst_Backend/commits2e0253636e2987a636f654b15443532570442cae))
### 🛠️ Code Refactor
* **all:** refactoring to remove monorepo taking to long to get it wokring as intended ([b15f1d8](https://git.tuffraid.net/cowch/Lst_Backend/commitsb15f1d8ae815f2eb232b2c3f3918d7e63490dcaf))
* **lst:** refactor to monolithic completed ([dae0071](https://git.tuffraid.net/cowch/Lst_Backend/commitsdae00716ecb3f87263e531ff559f980c5561543e))

View File

@@ -1,13 +1,13 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/lst.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Logistics Support Tool</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View File

@@ -14,6 +14,7 @@
"@radix-ui/react-slot": "^1.1.2",
"@tailwindcss/vite": "^4.0.6",
"@tanstack/react-query": "^5.66.5",
"@tanstack/react-router": "^1.106.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.475.0",
@@ -27,6 +28,8 @@
},
"devDependencies": {
"@eslint/js": "^9.19.0",
"@tanstack/router-devtools": "^1.106.0",
"@tanstack/router-plugin": "^1.106.0",
"@types/node": "^22.13.4",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",

BIN
frontend/public/lst.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -1,31 +0,0 @@
import LoginForm from "./components/LoginForm";
import {Button} from "./components/ui/button";
import {useSession} from "./lib/hooks/useSession";
import {useLogout} from "./lib/hooks/useLogout";
import "./styles.css";
function App() {
const {session, status} = useSession();
const logout = useLogout();
if (!session || status === "error") {
return (
<p>
no session please login <LoginForm />
</p>
);
}
return (
<>
{/* <p>Logged in user: {session.user.username}</p> */}
<>Session: {JSON.stringify(session)}</>
<p>Status: {JSON.stringify(status)}</p>
<p>
<Button onClick={() => logout()}>Logout</Button>
</p>
</>
);
}
export default App;

View File

@@ -1,5 +1,5 @@
import {useState} from "react";
import {useSessionStore} from "../lib/store/sessionStore";
import {useSessionStore} from "../../lib/store/sessionStore";
import {useQueryClient} from "@tanstack/react-query";
const LoginForm = () => {
@@ -36,7 +36,7 @@ const LoginForm = () => {
setSession(data.data.token);
// Refetch the session data to reflect the logged-in state
queryClient.invalidateQueries(["session"]);
queryClient.invalidateQueries();
setUsername("");
setPassword("");

View File

@@ -15,7 +15,7 @@ const fetchSession = async () => {
Authorization: `Bearer ${token}`,
},
});
console.log(res);
// console.log(res);
if (!res.ok) {
throw new Error("Session not found");
}
@@ -31,7 +31,7 @@ export const useSession = () => {
queryKey: ["session"],
queryFn: fetchSession,
enabled: !!token, // Prevents query if token is null
staleTime: 5 * 60 * 1000, // 5 mins
staleTime: 60 * 1000,
gcTime: 10 * 60 * 1000, // 10 mins
refetchOnWindowFocus: true,
});
@@ -47,3 +47,5 @@ export const useSession = () => {
return {session: data && token ? {user: data.user, token: data.token} : null, status, error};
};
export type SessionType = ReturnType<typeof useSession>;

View File

@@ -1,13 +1,38 @@
import {StrictMode} from "react";
import {createRoot} from "react-dom/client";
import ReactDOM from "react-dom/client";
import "./styles.css";
import App from "./App.tsx";
import {SessionProvider} from "./components/providers/Providers.tsx";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<SessionProvider>
<App />
</SessionProvider>
</StrictMode>
);
import {SessionProvider} from "./components/providers/Providers.tsx";
import {RouterProvider, createRouter} from "@tanstack/react-router";
// Import the generated route tree
import {routeTree} from "./routeTree.gen";
import {useSession} from "./lib/hooks/useSession.ts";
// Create a new router instance
const router = createRouter({routeTree, context: {sessionType: undefined!}});
// Register the router instance for type safety
declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}
function App() {
const sessionType = useSession();
return <RouterProvider router={router} context={{sessionType}} />;
}
// Render the app
const rootElement = document.getElementById("root")!;
if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement);
root.render(
<StrictMode>
<SessionProvider>
<App />
</SessionProvider>
</StrictMode>
);
}

View File

@@ -0,0 +1,311 @@
/* eslint-disable */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// This file was automatically generated by TanStack Router.
// You should NOT make any changes in this file as it will be overwritten.
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
// Import Routes
import { Route as rootRoute } from './routes/__root'
import { Route as LoginImport } from './routes/login'
import { Route as AboutImport } from './routes/about'
import { Route as AuthImport } from './routes/_auth'
import { Route as IndexImport } from './routes/index'
import { Route as OcpIndexImport } from './routes/ocp/index'
import { Route as OcpLotsImport } from './routes/ocp/lots'
import { Route as OcpLineIDImport } from './routes/ocp/$lineID'
import { Route as AuthProfileImport } from './routes/_auth/profile'
import { Route as AuthDashboardImport } from './routes/_auth/dashboard'
// Create/Update Routes
const LoginRoute = LoginImport.update({
id: '/login',
path: '/login',
getParentRoute: () => rootRoute,
} as any)
const AboutRoute = AboutImport.update({
id: '/about',
path: '/about',
getParentRoute: () => rootRoute,
} as any)
const AuthRoute = AuthImport.update({
id: '/_auth',
getParentRoute: () => rootRoute,
} as any)
const IndexRoute = IndexImport.update({
id: '/',
path: '/',
getParentRoute: () => rootRoute,
} as any)
const OcpIndexRoute = OcpIndexImport.update({
id: '/ocp/',
path: '/ocp/',
getParentRoute: () => rootRoute,
} as any)
const OcpLotsRoute = OcpLotsImport.update({
id: '/ocp/lots',
path: '/ocp/lots',
getParentRoute: () => rootRoute,
} as any)
const OcpLineIDRoute = OcpLineIDImport.update({
id: '/ocp/$lineID',
path: '/ocp/$lineID',
getParentRoute: () => rootRoute,
} as any)
const AuthProfileRoute = AuthProfileImport.update({
id: '/profile',
path: '/profile',
getParentRoute: () => AuthRoute,
} as any)
const AuthDashboardRoute = AuthDashboardImport.update({
id: '/dashboard',
path: '/dashboard',
getParentRoute: () => AuthRoute,
} as any)
// Populate the FileRoutesByPath interface
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/': {
id: '/'
path: '/'
fullPath: '/'
preLoaderRoute: typeof IndexImport
parentRoute: typeof rootRoute
}
'/_auth': {
id: '/_auth'
path: ''
fullPath: ''
preLoaderRoute: typeof AuthImport
parentRoute: typeof rootRoute
}
'/about': {
id: '/about'
path: '/about'
fullPath: '/about'
preLoaderRoute: typeof AboutImport
parentRoute: typeof rootRoute
}
'/login': {
id: '/login'
path: '/login'
fullPath: '/login'
preLoaderRoute: typeof LoginImport
parentRoute: typeof rootRoute
}
'/_auth/dashboard': {
id: '/_auth/dashboard'
path: '/dashboard'
fullPath: '/dashboard'
preLoaderRoute: typeof AuthDashboardImport
parentRoute: typeof AuthImport
}
'/_auth/profile': {
id: '/_auth/profile'
path: '/profile'
fullPath: '/profile'
preLoaderRoute: typeof AuthProfileImport
parentRoute: typeof AuthImport
}
'/ocp/$lineID': {
id: '/ocp/$lineID'
path: '/ocp/$lineID'
fullPath: '/ocp/$lineID'
preLoaderRoute: typeof OcpLineIDImport
parentRoute: typeof rootRoute
}
'/ocp/lots': {
id: '/ocp/lots'
path: '/ocp/lots'
fullPath: '/ocp/lots'
preLoaderRoute: typeof OcpLotsImport
parentRoute: typeof rootRoute
}
'/ocp/': {
id: '/ocp/'
path: '/ocp'
fullPath: '/ocp'
preLoaderRoute: typeof OcpIndexImport
parentRoute: typeof rootRoute
}
}
}
// Create and export the route tree
interface AuthRouteChildren {
AuthDashboardRoute: typeof AuthDashboardRoute
AuthProfileRoute: typeof AuthProfileRoute
}
const AuthRouteChildren: AuthRouteChildren = {
AuthDashboardRoute: AuthDashboardRoute,
AuthProfileRoute: AuthProfileRoute,
}
const AuthRouteWithChildren = AuthRoute._addFileChildren(AuthRouteChildren)
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'': typeof AuthRouteWithChildren
'/about': typeof AboutRoute
'/login': typeof LoginRoute
'/dashboard': typeof AuthDashboardRoute
'/profile': typeof AuthProfileRoute
'/ocp/$lineID': typeof OcpLineIDRoute
'/ocp/lots': typeof OcpLotsRoute
'/ocp': typeof OcpIndexRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'': typeof AuthRouteWithChildren
'/about': typeof AboutRoute
'/login': typeof LoginRoute
'/dashboard': typeof AuthDashboardRoute
'/profile': typeof AuthProfileRoute
'/ocp/$lineID': typeof OcpLineIDRoute
'/ocp/lots': typeof OcpLotsRoute
'/ocp': typeof OcpIndexRoute
}
export interface FileRoutesById {
__root__: typeof rootRoute
'/': typeof IndexRoute
'/_auth': typeof AuthRouteWithChildren
'/about': typeof AboutRoute
'/login': typeof LoginRoute
'/_auth/dashboard': typeof AuthDashboardRoute
'/_auth/profile': typeof AuthProfileRoute
'/ocp/$lineID': typeof OcpLineIDRoute
'/ocp/lots': typeof OcpLotsRoute
'/ocp/': typeof OcpIndexRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths:
| '/'
| ''
| '/about'
| '/login'
| '/dashboard'
| '/profile'
| '/ocp/$lineID'
| '/ocp/lots'
| '/ocp'
fileRoutesByTo: FileRoutesByTo
to:
| '/'
| ''
| '/about'
| '/login'
| '/dashboard'
| '/profile'
| '/ocp/$lineID'
| '/ocp/lots'
| '/ocp'
id:
| '__root__'
| '/'
| '/_auth'
| '/about'
| '/login'
| '/_auth/dashboard'
| '/_auth/profile'
| '/ocp/$lineID'
| '/ocp/lots'
| '/ocp/'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
AuthRoute: typeof AuthRouteWithChildren
AboutRoute: typeof AboutRoute
LoginRoute: typeof LoginRoute
OcpLineIDRoute: typeof OcpLineIDRoute
OcpLotsRoute: typeof OcpLotsRoute
OcpIndexRoute: typeof OcpIndexRoute
}
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
AuthRoute: AuthRouteWithChildren,
AboutRoute: AboutRoute,
LoginRoute: LoginRoute,
OcpLineIDRoute: OcpLineIDRoute,
OcpLotsRoute: OcpLotsRoute,
OcpIndexRoute: OcpIndexRoute,
}
export const routeTree = rootRoute
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()
/* ROUTE_MANIFEST_START
{
"routes": {
"__root__": {
"filePath": "__root.tsx",
"children": [
"/",
"/_auth",
"/about",
"/login",
"/ocp/$lineID",
"/ocp/lots",
"/ocp/"
]
},
"/": {
"filePath": "index.tsx"
},
"/_auth": {
"filePath": "_auth.tsx",
"children": [
"/_auth/dashboard",
"/_auth/profile"
]
},
"/about": {
"filePath": "about.tsx"
},
"/login": {
"filePath": "login.tsx"
},
"/_auth/dashboard": {
"filePath": "_auth/dashboard.tsx",
"parent": "/_auth"
},
"/_auth/profile": {
"filePath": "_auth/profile.tsx",
"parent": "/_auth"
},
"/ocp/$lineID": {
"filePath": "ocp/$lineID.tsx"
},
"/ocp/lots": {
"filePath": "ocp/lots.tsx"
},
"/ocp/": {
"filePath": "ocp/index.tsx"
}
}
}
ROUTE_MANIFEST_END */

View File

@@ -0,0 +1,42 @@
import {createRootRouteWithContext, Link, Outlet} from "@tanstack/react-router";
import {TanStackRouterDevtools} from "@tanstack/router-devtools";
import {SessionType} from "../lib/hooks/useSession";
type RouterContext = {
sessionType: SessionType;
};
// same as the layout
export const Route = createRootRouteWithContext<RouterContext>()({
component: () => {
return (
<>
<div className="p-2 flex gap-2">
<Link to="/" className="[&.active]:font-bold">
Home
</Link>{" "}
<Link to="/about" className="[&.active]:font-bold">
About
</Link>
<Link to="/dashboard" className="[&.active]:font-bold">
dashboard
</Link>
<Link to="/profile" className="[&.active]:font-bold">
{({isActive}) => <>Profile {isActive && "~"}</>}
</Link>
<Link
to="/ocp"
search={{q: "1"}}
activeProps={{style: {fontWeight: "bold"}}}
className="[&.active]:font-bold"
>
{({isActive}) => <>OCP {isActive && "~"}</>}
</Link>
</div>
<hr />
<Outlet />
<TanStackRouterDevtools />
</>
);
},
});

View File

@@ -0,0 +1,12 @@
import {createFileRoute, redirect} from "@tanstack/react-router";
// src/routes/_authenticated.tsx
export const Route = createFileRoute("/_auth")({
beforeLoad: async ({context}) => {
if (!context.sessionType.session) {
throw redirect({
to: "/",
});
}
},
});

View File

@@ -0,0 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/_auth/dashboard')({
component: RouteComponent,
})
function RouteComponent() {
return <div>Hello "/_auth/dashboard"!</div>
}

View File

@@ -0,0 +1,9 @@
import {createFileRoute} from "@tanstack/react-router";
export const Route = createFileRoute("/_auth/profile")({
component: RouteComponent,
});
function RouteComponent() {
return <div>Hello "/profile"!</div>;
}

View File

@@ -0,0 +1,13 @@
import {createFileRoute} from "@tanstack/react-router";
export const Route = createFileRoute("/about")({
component: About,
});
function About() {
return (
<div className="m-auto">
<h2>About page to come.</h2>
</div>
);
}

View File

@@ -0,0 +1,29 @@
import {createFileRoute} from "@tanstack/react-router";
import LoginForm from "../components/auth/LoginForm";
import {Button} from "../components/ui/button";
import {useLogout} from "../lib/hooks/useLogout";
import {useSession} from "../lib/hooks/useSession";
export const Route = createFileRoute("/")({
component: Index,
});
function Index() {
const {session} = useSession();
const logout = useLogout();
return (
<div className="p-2">
<h3>Welcome Home!</h3>
<br></br>
<p>
{session ? (
<>
<Button onClick={() => logout()}>Logout</Button>{" "}
</>
) : (
<LoginForm />
)}
</p>
</div>
);
}

View File

@@ -0,0 +1,38 @@
import {createFileRoute, useRouter} from "@tanstack/react-router";
import {isAuthenticated, signIn, signOut} from "../utils/auth";
import {Button} from "../components/ui/button";
export const Route = createFileRoute("/login")({
component: RouteComponent,
});
function RouteComponent() {
const router = useRouter();
return (
<div>
<h2>Ligin</h2>
{isAuthenticated() ? (
<>
<p>Hello User!</p>
<Button
onClick={async () => {
signOut();
router.invalidate();
}}
>
signOut
</Button>
</>
) : (
<Button
onClick={async () => {
signIn();
router.invalidate();
}}
>
Sign in
</Button>
)}
</div>
);
}

View File

@@ -0,0 +1,17 @@
import {createFileRoute} from "@tanstack/react-router";
export const Route = createFileRoute("/ocp/$lineID")({
component: RouteComponent,
loader: async ({params}) => {
await new Promise((r) => setTimeout(r, 1500));
//throw new Error();
return {lineID: params.lineID};
},
pendingComponent: () => <div className="m-auto">Loading....</div>,
errorComponent: () => <div className="m-auto">Error....</div>,
});
function RouteComponent() {
const {lineID} = Route.useLoaderData();
return <div>Hello "/ocp/{lineID}"!</div>;
}

View File

@@ -0,0 +1,34 @@
import {createFileRoute, Link} from "@tanstack/react-router";
export const Route = createFileRoute("/ocp/")({
component: RouteComponent,
validateSearch: (search) => {
return {
q: (search.q as string) || "",
};
},
loaderDeps: ({search: {q}}) => ({q}),
loader: async ({deps: {q}}) => {
return {line: q};
},
});
function RouteComponent() {
const {line} = Route.useLoaderData();
const lines = ["l", "2", "3"];
return (
<div>
<h2>Hello "/ocp/{line}/something"!</h2>
{lines.map((line) => {
return (
<div key={line}>
<Link to="/ocp/$lineID" params={{lineID: line}}>
Post
</Link>
</div>
);
})}
</div>
);
}

View File

@@ -0,0 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/ocp/lots')({
component: RouteComponent,
})
function RouteComponent() {
return <div>Hello "/ocp/lots"!</div>
}

View File

@@ -0,0 +1,11 @@
export function isAuthenticated() {
return localStorage.getItem("isAuthenticated") === "true";
}
export function signIn() {
return localStorage.setItem("isAuthenticated", "true");
}
export function signOut() {
return localStorage.removeItem("isAuthenticated");
}

View File

@@ -1,11 +1,12 @@
import {defineConfig} from "vite";
import react from "@vitejs/plugin-react-swc";
import tailwindcss from "@tailwindcss/vite";
import {TanStackRouterVite} from "@tanstack/router-plugin/vite";
import path from "path";
// https://vite.dev/config/
export default defineConfig({
plugins: [react(), tailwindcss()],
plugins: [react(), tailwindcss(), TanStackRouterVite({autoCodeSplitting: true})],
// build: {
// outDir: path.resolve(__dirname, "../../dist/frontend/dist"),
// emptyOutDir: true,

View File

@@ -1,6 +1,6 @@
{
"name": "lstv2",
"version": "1.0.0",
"version": "1.1.0",
"description": "",
"main": "index.ts",
"scripts": {
@@ -12,7 +12,7 @@
"build:server": "cd apps/server && bun build index.js --outdir ../../dist/server",
"build:ocme": "rimraf dist/ocme && cd apps/ocme && bun build index.js --outdir ../../dist/ocme",
"build:front": "cd frontend && rimraf frontend/dist && bun run build",
"start": "bun --env-file .env run ./server/index.js",
"start": "cd server && bun run --env-file ../.env ./index.js",
"commit": "cz",
"clean": "rimraf dist/server",
"deploy": "standard-version --conventional-commits"

View File

@@ -52,8 +52,8 @@ routes.forEach((route) => {
// return c.json({success: true, message: "is authenticated"});
// });
app.get("*", serveStatic({root: "./frontend/dist"}));
app.get("*", serveStatic({path: "./frontend/dist/index.html"}));
app.get("*", serveStatic({root: "../frontend/dist"}));
app.get("*", serveStatic({path: "../frontend/dist/index.html"}));
export default app;