first commit

This commit is contained in:
2026-01-26 10:14:10 +03:30
commit 9a995d5109
160 changed files with 34879 additions and 0 deletions

159
src/App.tsx Normal file
View File

@@ -0,0 +1,159 @@
import { useEffect, useMemo, useCallback } from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { RouterProvider } from "@tanstack/react-router";
import { ToastContainer } from "react-toastify";
import {
useUserProfileStore,
useUserStore,
} from "./context/zustand-store/userStore";
import { makeRouter } from "./routes/routes";
import { useDarkMode } from "./hooks/useDarkMode";
import { ItemWithSubItems } from "./types/userPermissions";
import { useFetchProfile } from "./hooks/useFetchProfile";
import { getInspectionMenuItems } from "./screen/SideBar";
const versionNumber = "/src/version.txt";
import "./index.css";
import "react-toastify/dist/ReactToastify.css";
import { checkIsMobile } from "./utils/checkIsMobile";
const queryClient = new QueryClient();
function AppContent() {
const auth = useUserStore((s) => s.auth);
const { profile } = useUserProfileStore();
const { getProfile } = useFetchProfile();
useEffect(() => {
if (auth && !profile) {
getProfile();
}
}, [auth, profile, getProfile]);
return null;
}
export default function App() {
const auth = useUserStore((s) => s.auth);
const { profile } = useUserProfileStore();
const [isDark] = useDarkMode();
const menuItems: ItemWithSubItems[] = useMemo(() => {
const userPermissions = profile?.permissions || [];
const permissionsArray = Array.isArray(userPermissions)
? userPermissions.filter((p): p is string => typeof p === "string")
: [];
return getInspectionMenuItems(permissionsArray);
}, [profile?.permissions]);
const router = useMemo(() => {
try {
const newRouter = makeRouter(auth ?? null);
if (!newRouter) {
console.error("Router creation returned null");
return makeRouter(null);
}
return newRouter;
} catch (error) {
console.error("Router creation error:", error);
try {
return makeRouter(null);
} catch (fallbackError) {
console.error("Fallback router creation failed:", fallbackError);
return null;
}
}
}, [auth, menuItems]);
const hardRefresh = useCallback(() => {
const url = new URL(window.location.href);
url.searchParams.set("refresh", Date.now().toString());
window.location.href = url.toString();
}, []);
const runWhenIdle = useCallback((fn: () => void) => {
const ric = (window as any).requestIdleCallback;
if (typeof ric === "function") {
ric(fn);
} else {
setTimeout(fn, 300);
}
}, []);
useEffect(() => {
let aborted = false;
const controller = new AbortController();
const checkVersion = () => {
if (document.visibilityState !== "visible") return;
fetch(versionNumber + `?_=${Date.now()}`, {
signal: controller.signal,
cache: "no-store",
})
.then((res) => res.text())
.then(async (txt) => {
if (aborted) return;
const latest = txt.trim();
const stored = localStorage.getItem("AppVersion");
if (latest && latest !== stored) {
localStorage.setItem("AppVersion", latest);
const clearAndReload = async () => {
if ("caches" in window) {
const names = await caches.keys();
for (const n of names) {
await caches.delete(n).catch(() => undefined);
}
}
setTimeout(hardRefresh, 200);
};
runWhenIdle(clearAndReload);
}
})
.catch(() => {});
};
runWhenIdle(checkVersion);
return () => {
aborted = true;
controller.abort();
};
}, [hardRefresh, runWhenIdle]);
useEffect(() => {
const url = new URL(window.location.href);
if (url.searchParams.has("refresh")) {
url.searchParams.delete("refresh");
window.history.replaceState(
{},
document.title,
url.pathname + url.search
);
}
}, []);
if (!router) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-center">
<h1 className="text-2xl font-bold mb-4">در حال بارگذاری...</h1>
</div>
</div>
);
}
return (
<div
className={
isDark ? "dark:bg-dark-900 dark-scrollbar" : "bg-white light-scrollbar"
}
>
<QueryClientProvider client={queryClient}>
<AppContent />
<RouterProvider router={router} />
</QueryClientProvider>
<ToastContainer position="bottom-right" rtl={true} theme="light" />
{checkIsMobile() && <div className="h-20"></div>}
</div>
);
}