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

61
src/utils/axios.ts Normal file
View File

@@ -0,0 +1,61 @@
import axios from "axios";
import { useUserStore } from "../context/zustand-store/userStore";
import { toast } from "react-toastify";
import { checkIsMobile } from "./checkIsMobile";
let hasShownUnauthorizedToast = false;
const api = axios.create({
baseURL: "https://inspectionbackend.rasadyar.com/",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
withCredentials: true,
});
api.interceptors.request.use(
(config) => {
const token = useUserStore.getState().auth;
if (token) {
config.headers.Authorization = `${token}`;
}
// If URL is a full URL (starts with http:// or https://), it's an external API
// Clear baseURL and disable credentials to avoid CORS issues
if (
config.url?.startsWith("http://") ||
config.url?.startsWith("https://")
) {
config.baseURL = "";
config.withCredentials = false;
}
return config;
},
(error) => Promise.reject(error)
);
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401 && useUserStore.getState().auth) {
const logOut = useUserStore.getState().logOut;
if (!hasShownUnauthorizedToast) {
hasShownUnauthorizedToast = true;
toast.error("مجددا وارد شوید!", {
position: checkIsMobile() ? "bottom-center" : "top-center",
theme: "light",
rtl: true,
});
if (typeof logOut === "function") {
logOut();
}
}
}
return Promise.reject(error);
}
);
export default api;

View File

@@ -0,0 +1,9 @@
export function checkIsMobile(): boolean {
if (typeof navigator === "undefined") return false;
const userAgent =
navigator.userAgent || navigator.vendor || (window as any).opera;
return /android|iphone|ipad|ipod|opera mini|iemobile|mobile/i.test(userAgent);
}

View File

@@ -0,0 +1,47 @@
export const checkMenuPermission = (
menuItemName: string,
userPermissions: string[] = []
): boolean => {
if (userPermissions.includes("admin")) {
return true;
}
const permissionMap: Record<string, string[]> = {
users: ["admin", "add"],
inspections: ["admin", "submit"],
statics: [],
nationalinfo: ["admin"],
};
const requiredPermissions = permissionMap[menuItemName] || [];
if (requiredPermissions.length === 0) {
return true;
}
return requiredPermissions.some((perm) => userPermissions.includes(perm));
};
export const checkRoutePermission = (
routePath: string,
userPermissions: string[] = []
): boolean => {
if (userPermissions.includes("admin")) {
return true;
}
const routePermissionMap: Record<string, string[]> = {
"/users": ["admin", "add"],
"/inspections": ["admin", "submit"],
"/statics": [],
"/nationalinfo": ["admin"],
};
const requiredPermissions = routePermissionMap[routePath] || [];
if (requiredPermissions.length === 0) {
return true;
}
return requiredPermissions.some((perm) => userPermissions.includes(perm));
};

90
src/utils/formatTime.ts Normal file
View File

@@ -0,0 +1,90 @@
import { format } from "date-fns-jalali";
export const formatTime = (time: any) => {
const date = new Date(time);
const hours = date.getHours().toString().padStart(2, "0");
const minutes = date.getMinutes().toString().padStart(2, "0");
return format(new Date(time), "yyyy/MM/dd ") + `(${hours}:${minutes})`;
};
export const formatJustDate = (time: any) => {
if (time) {
return format(new Date(time), "yyyy/MM/dd");
} else {
return null;
}
};
export const formatJustTime = (time: any) => {
return format(new Date(time), "HH:MM");
};
export function formatStampDate(timestamp: number) {
const date = new Date(timestamp);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}/${month}/${day}`;
}
export function formatStampDateTime(timestamp: number) {
const date = new Date(timestamp);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hours = date.getHours().toString().padStart(2, "0");
const minutes = date.getMinutes().toString().padStart(2, "0");
return `${year}/${month}/${day} ${hours}:${minutes}`;
}
export const formatAgeCalcuation = (dateString: string) => {
const birthDate = new Date(dateString);
const currentDate = new Date();
let years = currentDate.getFullYear() - birthDate.getFullYear();
let months = currentDate.getMonth() - birthDate.getMonth();
let days = currentDate.getDate() - birthDate.getDate();
if (days < 0) {
months--;
const lastMonth = new Date(
currentDate.getFullYear(),
currentDate.getMonth(),
0
);
days += lastMonth.getDate();
}
if (months < 0) {
years--;
months += 12;
}
const parts: string[] = [];
if (years > 0) {
parts.push(`${years} سال`);
}
if (months > 0) {
parts.push(`${months} ماه`);
}
if (days > 0) {
parts.push(`${days} روز`);
}
if (parts.length === 0) {
return "0 روز";
}
return parts.join(" و ");
};
export function convertToIranianTime(dateString: Date | string): string {
const gregorianDate = new Date(dateString);
const jalaliDate = format(gregorianDate, "yyyy/MM/dd");
return jalaliDate;
}

View File

@@ -0,0 +1,8 @@
export function getBase64ImageSrc(
base64: string,
mimeType = "image/png"
): string {
return `data:${mimeType};base64,${base64}`;
}

View File

@@ -0,0 +1,32 @@
export function getCitiesOfProvince(province: string): string[] {
if (province === "hamedan") {
return [
"همدان",
"فامنین",
"رزن",
"درگزین",
"اسدآباد",
"بهار",
"تویسرکان",
"نهاوند",
"ملایر",
"کبودر آهنگ",
];
} else if (province === "markazi") {
return [
"اراك",
"خمین",
"دلیجان",
"شازند",
"خنداب",
"کمیجان",
"فراهان",
"تفرش",
"آشتیان",
"ساوه",
"زرندیه",
];
}
return [];
}

View File

@@ -0,0 +1,37 @@
export interface CityInfo {
fa: string;
en: string;
}
export function getCitiesOfProvinceInfo(province: string): CityInfo[] {
if (province === "hamedan") {
return [
{ fa: "همدان", en: "hamedan" },
{ fa: "فامنین", en: "famenin" },
{ fa: "رزن", en: "razan" },
{ fa: "درگزین", en: "dargazin" },
{ fa: "اسدآباد", en: "asadabad" },
{ fa: "بهار", en: "bahar" },
{ fa: "تویسرکان", en: "tuyserkan" },
{ fa: "نهاوند", en: "nehavand" },
{ fa: "ملایر", en: "malayer" },
{ fa: "کبودر آهنگ", en: "kabudar-ahang" },
];
} else if (province === "markazi") {
return [
{ fa: "اراک", en: "arak" },
{ fa: "خمین", en: "khomein" },
{ fa: "دلیجان", en: "delijan" },
{ fa: "شازند", en: "shazand" },
{ fa: "خنداب", en: "khandab" },
{ fa: "کمیجان", en: "komeijan" },
{ fa: "فراهان", en: "farahan" },
{ fa: "تفرش", en: "tafresh" },
{ fa: "آشتیان", en: "ashtian" },
{ fa: "ساوه", en: "saveh" },
{ fa: "زرندیه", en: "zarandieh" },
];
}
return [];
}

View File

@@ -0,0 +1,49 @@
export function getFaCityName(enCityName: string): string {
switch (enCityName) {
case "hamedan":
return "همدان";
case "famenin":
return "فامنین";
case "razan":
return "رزن";
case "dargazin":
return "درگزین";
case "asadabad":
return "اسدآباد";
case "bahar":
return "بهار";
case "tuyserkan":
return "تویسرکان";
case "nehavand":
return "نهاوند";
case "malayer":
return "ملایر";
case "kabudar-ahang":
return "کبودر آهنگ";
case "arak":
return "اراک";
case "khomein":
return "خمین";
case "delijan":
return "دلیجان";
case "shazand":
return "شازند";
case "khandab":
return "خنداب";
case "komeijan":
return "کمیجان";
case "farahan":
return "فراهان";
case "tafresh":
return "تفرش";
case "ashtian":
return "آشتیان";
case "saveh":
return "ساوه";
case "zarandieh":
return "زرندیه";
default:
return "نامشخص";
}
}

View File

@@ -0,0 +1,40 @@
export function getFaPermissions(permission: string) {
let faPermission = "";
switch (permission) {
case "users":
faPermission = "کاربران";
break;
case "inspections":
faPermission = "بازرسی‌ها";
break;
case "statics":
faPermission = "آمار";
break;
case "nationalinfo":
faPermission = "اطلاعات افراد";
break;
case "ladinginfo":
faPermission = "اطلاعات بارنامه";
break;
case "veterinarytransfer":
faPermission = "اطلاعات گواهی حمل";
break;
case "main":
faPermission = "صفحه اصلی";
break;
case "add":
faPermission = "افزودن کاربر";
break;
case "submit":
faPermission = "ثبت بازرسی";
break;
case "admin":
faPermission = "مدیر";
break;
default:
faPermission = permission;
break;
}
return faPermission;
}

View File

@@ -0,0 +1,13 @@
export function getFaProvince(province: string): string {
switch (province) {
case "hamedan":
return "همدان";
case "markazi":
return "مركزی";
case "lorestan":
return "لرستان";
default:
return "نامشخص";
}
}

View File

@@ -0,0 +1,14 @@
export function getLocationByProvince(province: string): [number, number] {
switch (province) {
case "hamedan":
return [34.79759408342567, 48.51519584655762];
case "markazi":
return [34.089416725974985, 49.68841552734375];
case "lorestan":
return [33.48238560115483, 48.35046866139527];
default:
return [33.48238560115483, 48.35046866139527];
}
}

View File

@@ -0,0 +1,10 @@
export const getPosSellStatus = (item: any): number => {
const realAllocated = parseInt(item?.pos?.products?.[0]?.real_allocated_weight || "0");
const totalCarcasses = parseInt(item?.pos?.products?.[0]?.total_carcasses_weight || "0");
if (totalCarcasses === 0) return 0;
return Math.round((realAllocated / totalCarcasses) * 100);
};

9
src/utils/getProgress.ts Normal file
View File

@@ -0,0 +1,9 @@
export const getProgress = (item: any): number => {
const totalQuantity = item?.hatching?.[0]?.quantity || 0;
const leftOver = item?.hatching?.[0]?.left_over || 0;
const progress = totalQuantity ? (leftOver / totalQuantity) * 100 : 0;
return progress;
};

View File

@@ -0,0 +1,16 @@
// Helper to get backend address for province
export const getSystemBaseAddress = (province: string): string => {
let backendAddress;
if (province === "markazi") {
backendAddress = "https://mabackend.rasadyar.com/";
} else if (province === "hamedan") {
backendAddress = "https://habackend.rasadyar.com/";
} else if (province === "bushehr") {
backendAddress = "https://bubackend.rasadyar.com/";
} else {
backendAddress = "https://habackend.rasadyar.com/";
}
return backendAddress;
};

View File

@@ -0,0 +1,65 @@
import { useQuery, useMutation } from "@tanstack/react-query";
import { useBackdropStore } from "../context/zustand-store/appStore";
import api from "./axios";
import { AxiosError } from "axios";
type RequestParams = {
api: string;
method?: "get" | "post" | "put" | "delete" | "patch";
params?: any;
queryKey?: any[];
enabled?: boolean;
disableBackdrop?: boolean;
};
export function useApiRequest<TData = any>({
api: url,
params,
queryKey = [url],
enabled = true,
disableBackdrop = false,
}: RequestParams) {
const { openBackdrop, closeBackdrop } = useBackdropStore();
return useQuery<TData>({
queryKey,
queryFn: async () => {
if (!disableBackdrop && window.location.pathname !== "/") openBackdrop();
try {
const response = await api.get(url, { params });
return response.data;
} finally {
if (!disableBackdrop) closeBackdrop();
}
},
enabled,
retry: 3,
});
}
export function useApiMutation<TData = any>({
api: url,
method = "post",
disableBackdrop = false,
}: RequestParams) {
const { openBackdrop, closeBackdrop } = useBackdropStore();
return useMutation<TData, AxiosError, any>({
mutationFn: async (params) => {
if (!disableBackdrop) openBackdrop();
try {
const response = await api.request({
url,
method,
data: ["post", "put", "patch"].includes(method) ? params : undefined,
params: ["get", "delete"].includes(method) ? params : undefined,
});
return response.data;
} finally {
if (!disableBackdrop) closeBackdrop();
}
},
});
}