refactor(app): changed ways we get data so we can have better reasons why app no worky

This commit is contained in:
2026-05-13 20:49:43 -05:00
parent e7af3d1182
commit 30ff7b71d9
11 changed files with 284 additions and 9 deletions

View File

@@ -7,6 +7,7 @@
<title>Logistics Support Tool</title>
</head>
<body>
<script>
const configScript = document.createElement("script");
configScript.src = `${window.location.origin}/lst/api/lst-config.js`;

View File

@@ -19,11 +19,14 @@ export default function Header() {
const { data: session } = useSession();
const { signOut } = authClient;
const router = useRouterState();
const navigate = useNavigate();
const currentPath = router.location.href;
return (
<header className="sticky top-0 z-50 flex w-full items-center border-b bg-background">
<header
className={`sticky top-0 z-50 flex w-full items-center border-b ${session?.session.impersonatedBy ? "bg-amber-600" : "bg-background"} `}
>
<div className="flex justify-between w-full">
<div className="flex items-center gap-2 px-4">
<div className="flex flex-row">
@@ -48,6 +51,20 @@ export default function Header() {
<span className="font-semibold text-2xl">Logistics Support Tool</span>
</div>
<div className="m-1 flex gap-1">
<div>
{session?.session.impersonatedBy && (
<Button
onClick={async () => {
await authClient.admin.stopImpersonating();
await authClient.getSession();
window.location.assign("/lst/app/admin/users");
}}
>
Stop Impersonating
</Button>
)}
</div>
<div>
<ModeToggle />
</div>

View File

@@ -0,0 +1,51 @@
import { useRouter } from "@tanstack/react-router";
import { Card, CardContent, CardHeader } from "./ui/card";
export default function NotFound() {
const router = useRouter();
let url: string;
if (window.location.origin.includes("localhost")) {
url = `https://www.youtube.com/watch?v=dQw4w9WgXcQ`;
} else if (window.location.origin.includes("vms006")) {
url = `https://${window.location.hostname.replace("vms006", "prod.alpla.net/")}lst/app/old`;
} else {
url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ";
}
return (
<div className="flex items-center justify-center bg-background text-foreground">
<Card>
<CardHeader>
<p className="text-2xl">
Oops, Looks like you hit a link you shouldn't have
</p>
</CardHeader>
<CardContent>
<p className="mt-3 text-muted-foreground">
Your have tried to go to a page that you are not authorized to be
at.
</p>
<div className="flex justify-center">
<div>
<a href={`${url}`} target="_blank" rel="noopener">
<b>
<strong>OLD - LST Home</strong>
</b>
</a>
</div>
<div>
<button
type="button"
className="w-64"
onClick={() => router.navigate({ to: "/", replace: true })}
>
<strong>Home</strong>
</button>
</div>
</div>
</CardContent>
</Card>
</div>
);
}

View File

@@ -1,6 +1,7 @@
import { useSuspenseQuery } from "@tanstack/react-query";
import { Link } from "@tanstack/react-router";
import { Bell, Logs, Server, Settings, UsersRound } from "lucide-react";
import { getSettings } from "../../lib/queries/getSettings";
import {
SidebarGroup,
SidebarGroupContent,
@@ -23,6 +24,7 @@ import {
export default function AdminSidebar({ session }: any) {
const { setOpen } = useSidebar();
const { data: settings, isLoading } = useSuspenseQuery(getSettings());
const items = [
{
title: "Notifications",
@@ -70,7 +72,9 @@ export default function AdminSidebar({ session }: any) {
icon: UsersRound,
role: ["systemAdmin", "admin", "manager"],
module: "admin",
active: true,
active:
!isLoading &&
settings.filter((n: any) => n.name === "mobile")[0].active,
},
];
return (
@@ -80,7 +84,7 @@ export default function AdminSidebar({ session }: any) {
<SidebarMenu>
{items.map((item) => (
<div key={item.title}>
{item.role.includes(session.user.role) && (
{item.role.includes(session.user.role) && item.active && (
<SidebarMenuItem>
<SidebarMenuButton asChild>
<Link to={item.url} onClick={() => setOpen(false)}>

View File

@@ -0,0 +1,40 @@
import type { Router } from "@tanstack/react-router";
import axios from "axios";
import { toast } from "sonner";
let appRouter: Router<any, any> | null = null;
export function setApiRouter(router: Router<any, any>) {
appRouter = router;
}
export const api = axios.create({
baseURL: "/lst/api",
withCredentials: true,
timeout: 15000,
});
api.interceptors.response.use(
(response) => response,
(error) => {
const isNetworkError =
error.code === "ERR_NETWORK" ||
error.code === "ECONNABORTED" ||
error.message === "Network Error" ||
error.message === "Failed to fetch" ||
!error.response;
if (error.response?.status === 403) {
// redirect, toast, or show forbidden page
toast.error("Unauthorized to be here");
appRouter?.navigate({ to: "/forbidden", replace: true });
}
if (isNetworkError) {
appRouter?.navigate({ to: "/app-down", replace: true });
}
return Promise.reject(error);
},
);

View File

@@ -1,3 +1,4 @@
import { redirect } from "@tanstack/react-router";
import { adminClient, genericOAuthClient } from "better-auth/client/plugins";
import { createAuthClient } from "better-auth/react";
import { ac, admin, manager, systemAdmin, user } from "./auth-permissions";
@@ -16,6 +17,14 @@ export const authClient = createAuthClient({
}),
genericOAuthClient(),
],
fetchOptions: {
onError() {
redirect({
to: "/app-down",
replace: true,
});
},
},
});
export const { useSession, signUp, signIn, signOut } = authClient;

View File

@@ -3,6 +3,8 @@ import { StrictMode } from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import { createRouter, RouterProvider } from "@tanstack/react-router";
import NotFound from "./components/NotFound";
import { setApiRouter } from "./lib/apiHelper";
import socket from "./lib/socket.io";
import { loadUmami } from "./lib/umami.utils";
// Import the generated route tree
@@ -13,8 +15,9 @@ const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5,
retry: 0,
retry: 2,
refetchOnWindowFocus: true,
retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 5000),
},
},
});
@@ -27,8 +30,11 @@ const router = createRouter({
context: {
queryClient,
},
defaultNotFoundComponent: NotFound,
});
setApiRouter(router);
// Register the router instance for type safety
declare module "@tanstack/react-router" {
interface Register {

View File

@@ -9,6 +9,8 @@
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { Route as rootRouteImport } from './routes/__root'
import { Route as ForbiddenRouteImport } from './routes/forbidden'
import { Route as AppDownRouteImport } from './routes/app-down'
import { Route as AboutRouteImport } from './routes/about'
import { Route as IndexRouteImport } from './routes/index'
import { Route as DocsIndexRouteImport } from './routes/docs/index'
@@ -24,6 +26,16 @@ 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 ForbiddenRoute = ForbiddenRouteImport.update({
id: '/forbidden',
path: '/forbidden',
getParentRoute: () => rootRouteImport,
} as any)
const AppDownRoute = AppDownRouteImport.update({
id: '/app-down',
path: '/app-down',
getParentRoute: () => rootRouteImport,
} as any)
const AboutRoute = AboutRouteImport.update({
id: '/about',
path: '/about',
@@ -98,6 +110,8 @@ const authUserProfileRoute = authUserProfileRouteImport.update({
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/about': typeof AboutRoute
'/app-down': typeof AppDownRoute
'/forbidden': typeof ForbiddenRoute
'/login': typeof authLoginRoute
'/admin/logs': typeof AdminLogsRoute
'/admin/notifications': typeof AdminNotificationsRoute
@@ -114,6 +128,8 @@ export interface FileRoutesByFullPath {
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/about': typeof AboutRoute
'/app-down': typeof AppDownRoute
'/forbidden': typeof ForbiddenRoute
'/login': typeof authLoginRoute
'/admin/logs': typeof AdminLogsRoute
'/admin/notifications': typeof AdminNotificationsRoute
@@ -131,6 +147,8 @@ export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/about': typeof AboutRoute
'/app-down': typeof AppDownRoute
'/forbidden': typeof ForbiddenRoute
'/(auth)/login': typeof authLoginRoute
'/admin/logs': typeof AdminLogsRoute
'/admin/notifications': typeof AdminNotificationsRoute
@@ -149,6 +167,8 @@ export interface FileRouteTypes {
fullPaths:
| '/'
| '/about'
| '/app-down'
| '/forbidden'
| '/login'
| '/admin/logs'
| '/admin/notifications'
@@ -165,6 +185,8 @@ export interface FileRouteTypes {
to:
| '/'
| '/about'
| '/app-down'
| '/forbidden'
| '/login'
| '/admin/logs'
| '/admin/notifications'
@@ -181,6 +203,8 @@ export interface FileRouteTypes {
| '__root__'
| '/'
| '/about'
| '/app-down'
| '/forbidden'
| '/(auth)/login'
| '/admin/logs'
| '/admin/notifications'
@@ -198,6 +222,8 @@ export interface FileRouteTypes {
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
AboutRoute: typeof AboutRoute
AppDownRoute: typeof AppDownRoute
ForbiddenRoute: typeof ForbiddenRoute
authLoginRoute: typeof authLoginRoute
AdminLogsRoute: typeof AdminLogsRoute
AdminNotificationsRoute: typeof AdminNotificationsRoute
@@ -214,6 +240,20 @@ export interface RootRouteChildren {
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/forbidden': {
id: '/forbidden'
path: '/forbidden'
fullPath: '/forbidden'
preLoaderRoute: typeof ForbiddenRouteImport
parentRoute: typeof rootRouteImport
}
'/app-down': {
id: '/app-down'
path: '/app-down'
fullPath: '/app-down'
preLoaderRoute: typeof AppDownRouteImport
parentRoute: typeof rootRouteImport
}
'/about': {
id: '/about'
path: '/about'
@@ -318,6 +358,8 @@ declare module '@tanstack/react-router' {
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
AboutRoute: AboutRoute,
AppDownRoute: AppDownRoute,
ForbiddenRoute: ForbiddenRoute,
authLoginRoute: authLoginRoute,
AdminLogsRoute: AdminLogsRoute,
AdminNotificationsRoute: AdminNotificationsRoute,

View File

@@ -0,0 +1,51 @@
import { createFileRoute, useRouter } from "@tanstack/react-router";
import z from "zod";
import { Button } from "../components/ui/button";
import { Card, CardContent, CardHeader } from "../components/ui/card";
import { trackLstEvent } from "../lib/umami.utils";
export const Route = createFileRoute("/app-down")({
validateSearch: z.object({
redirect: z.string().optional(),
}),
component: RouteComponent,
});
function RouteComponent() {
const search = Route.useSearch();
const redirectPath = search.redirect ?? "/";
const router = useRouter();
const click = () => {
trackLstEvent("app_down_click", {
module: "app",
action: "click",
label: "redirect",
page: window.location.pathname,
});
router.navigate({ to: redirectPath, replace: true });
};
return (
<div className="flex items-center justify-center bg-background text-foreground">
<Card>
<CardHeader>
<p className="text-2xl">Oops, you shouldn't have done that</p>
</CardHeader>
<CardContent>
<p className="mt-3 text-muted-foreground">
Your have tried to go to a page that you are not authorized to be
at.
</p>
</CardContent>
<div className=" flex justify-center">
<Button
className="mt-5 rounded-md bg-primary px-4 py-2 text-primary-foreground"
onClick={click}
>
Refresh
</Button>
</div>
</Card>
</div>
);
}

View File

@@ -0,0 +1,41 @@
import { createFileRoute, useRouter } from "@tanstack/react-router";
import { Button } from "../components/ui/button";
import { Card, CardContent, CardHeader } from "../components/ui/card";
import { trackLstEvent } from "../lib/umami.utils";
export const Route = createFileRoute("/forbidden")({
component: RouteComponent,
});
function RouteComponent() {
const click = () => {
trackLstEvent("forbidden_click", {
module: "forbidden",
action: "click",
label: "redirect",
page: window.location.pathname,
});
router.navigate({ to: "/", replace: true });
};
const router = useRouter();
return (
<div className="flex items-center justify-center bg-background text-foreground">
<Card>
<CardHeader>
<p className="text-2xl">Oops, you shouldn't have done that</p>
</CardHeader>
<CardContent>
<p className="mt-3 text-muted-foreground">
Your have tried to go to a page that you are not authorized to be
at.
</p>
</CardContent>
<div className=" flex justify-center">
<Button className="w-64" onClick={click}>
Go Back
</Button>
</div>
</Card>
</div>
);
}

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from "@tanstack/react-router";
import z from "zod";
import { Button } from "../components/ui/button";
import { useSession } from "../lib/auth-client";
import { trackLstEvent } from "../lib/umami.utils";
import { runtimeConfig, trackLstEvent } from "../lib/umami.utils";
export const Route = createFileRoute("/")({
validateSearch: z.object({
@@ -14,7 +14,7 @@ export const Route = createFileRoute("/")({
});
function Index() {
const { isPending } = useSession();
const { data: session, isPending } = useSession();
if (isPending)
return <div className="flex justify-center mt-10">Loading...</div>;
@@ -38,6 +38,16 @@ function Index() {
});
};
const checkConfig = () => {
console.log(runtimeConfig);
trackLstEvent("config_click", {
module: "app",
action: "click",
label: "configCheck",
page: window.location.pathname,
});
};
return (
<div className="flex justify-center m-10 flex-col">
<h3 className="w-2xl text-3xl">Welcome Lst - V3</h3>
@@ -55,7 +65,7 @@ function Index() {
<strong>Click</strong>
</b>
</a>{" "}
<button onClick={click}>
<button onClick={click} type="button">
<a
href={`https://www.youtube.com/watch?v=dQw4w9WgXcQ`}
target="_blank"
@@ -67,6 +77,9 @@ function Index() {
</a>
</button>
</p>
{session && session.user.role === "systemAdmin" && (
<Button onClick={checkConfig}>Check config</Button>
)}
</div>
);
}