refactor(app): changed ways we get data so we can have better reasons why app no worky
This commit is contained in:
@@ -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`;
|
||||
|
||||
@@ -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>
|
||||
|
||||
51
frontend/src/components/NotFound.tsx
Normal file
51
frontend/src/components/NotFound.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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)}>
|
||||
|
||||
40
frontend/src/lib/apiHelper.ts
Normal file
40
frontend/src/lib/apiHelper.ts
Normal 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);
|
||||
},
|
||||
);
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
51
frontend/src/routes/app-down.tsx
Normal file
51
frontend/src/routes/app-down.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
41
frontend/src/routes/forbidden.tsx
Normal file
41
frontend/src/routes/forbidden.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user