Vitron-Front/app/utils/helpers.ts
2026-04-29 01:44:16 +03:30

295 lines
7.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { ProductDetail } from "src/api/types";
import jMoment from "moment-jalaali";
import { NavigateFunction } from "@remix-run/react";
export function detectPage(pathname: string | undefined) {
if (!pathname) return "home";
if (pathname === "/" || pathname === "") return "home";
const firstPath = pathname.split("/")[1];
if (firstPath === "explore") return "explore";
if (firstPath === "search") return "search";
if (firstPath === "profile") return "profile";
if (firstPath === "cart") return "cart";
if (firstPath === "store") {
// Check for store sub-pages
const storePath = pathname.split("/")[3];
if (storePath === "setting") return "setting";
if (storePath === "orders") return "orders";
if (storePath === "products") return "products";
if (storePath === "financial-dashboard") return "financial-dashboard";
return "store";
}
return "home"; // Default to home if no match
}
// Cache for formatPrice to avoid repeated conversions
const priceCache = new Map<string, string>();
const CACHE_MAX_SIZE = 1000;
export const formatPrice = (price: string) => {
// Check cache first
if (priceCache.has(price)) {
return priceCache.get(price)!;
}
const num = Number(price);
// Use English locale to get proper comma placement, then convert to Persian digits
const formatted = num.toLocaleString("en-US");
// Convert English digits to Persian digits
const persianNumbers = ["۰", "۱", "۲", "۳", "۴", "۵", "۶", "۷", "۸", "۹"];
const persianDigits = formatted.replace(/[0-9]/g, (digit) => {
return persianNumbers[parseInt(digit)];
});
// Store in cache with size limit
if (priceCache.size >= CACHE_MAX_SIZE) {
const firstKey = priceCache.keys().next().value;
priceCache.delete(firstKey || "");
}
priceCache.set(price, persianDigits);
return persianDigits;
};
// Helper to find matching product variant ID based on product attributes
export const findVariantId = (
product: ProductDetail,
selectedColor: string | null,
selectedSize: string | null
): string | null => {
// Find matching variant
const matchingVariant = product.variants?.find((variant) => {
let colorMatch = true;
let sizeMatch = true;
if (selectedColor) {
// Convert hex to Persian and compare with resolved رنگ
colorMatch =
!selectedColor || variant.resolvedAttributes?.COLOR === selectedColor;
}
if (selectedSize) {
// Direct comparison with resolved سایز
sizeMatch = variant.resolvedAttributes?.SIZE === selectedSize;
}
return colorMatch && sizeMatch;
});
return matchingVariant?.id || null;
};
export const getPersianTextOfPrice = (price: string) => {
if (!price || parseInt(price) === 0) return "صفر تومان";
if (price.length > 12) return "مبلغ بیش از حد مجاز";
const persianDigits = [
"صفر",
"یک",
"دو",
"سه",
"چهار",
"پنج",
"شش",
"هفت",
"هشت",
"نه",
];
const persianTens = [
"",
"ده",
"بیست",
"سی",
"چهل",
"پنجاه",
"شصت",
"هفتاد",
"هشتاد",
"نود",
];
const persianTeens = [
"ده",
"یازده",
"دوازده",
"سیزده",
"چهارده",
"پانزده",
"شانزده",
"هفده",
"هجده",
"نوزده",
];
const persianHundreds = [
"",
"صد",
"دویست",
"سیصد",
"چهارصد",
"پانصد",
"ششصد",
"هفتصد",
"هشتصد",
"نهصد",
];
const persianScales = ["", "هزار", "میلیون", "میلیارد"];
const num = parseInt(price);
const convertGroup = (n: number): string => {
if (n === 0) return "";
let result = "";
// Hundreds
if (n >= 100) {
result += persianHundreds[Math.floor(n / 100)] + " ";
n %= 100;
}
// Tens and units
if (n >= 10 && n < 20) {
result += persianTeens[n - 10] + " ";
} else {
if (n >= 20) {
result += persianTens[Math.floor(n / 10)] + " ";
n %= 10;
}
if (n > 0) {
result += persianDigits[n] + " ";
}
}
return result.trim();
};
const convertToWords = (n: number): string => {
if (n === 0) return "صفر";
let result = "";
let groupIndex = 0;
while (n > 0) {
const group = n % 1000;
if (group !== 0) {
const groupText = convertGroup(group);
if (groupIndex > 0) {
result =
groupText +
" " +
persianScales[groupIndex] +
(result ? " و " + result : "");
} else {
result = groupText;
}
}
n = Math.floor(n / 1000);
groupIndex++;
}
return result;
};
return convertToWords(num) + " تومان";
};
// Format date function to convert to Persian date format
export const formatDate = (dateString: string) => {
return jMoment(dateString).format("jYYYY/jM/jD");
};
// Format timestamp to a readable time format
export const formatTime = (timestamp: string) => {
if (!timestamp) return "";
const date = new Date(timestamp);
return date.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
hour12: false,
});
};
// Format timestamp to show relative time (like "2 minutes ago")
export const formatRelativeTime = (timestamp: string) => {
if (!timestamp) return "";
jMoment.locale("fa");
return jMoment(timestamp).fromNow();
};
// Format timestamp to a readable date format
export const formatDate2 = (timestamp: string) => {
if (!timestamp) return "";
const date = new Date(timestamp);
return date.toLocaleDateString("fa-IR", {
year: "numeric",
month: "long",
day: "numeric",
});
};
// Check if two timestamps are from different days
export const isDifferentDay = (timestamp1: string, timestamp2?: string) => {
if (!timestamp1 || !timestamp2) return false;
const date1 = new Date(timestamp1);
const date2 = new Date(timestamp2);
return (
date1.getFullYear() !== date2.getFullYear() ||
date1.getMonth() !== date2.getMonth() ||
date1.getDate() !== date2.getDate()
);
};
export const onSearch = (searchValue: string, navigate: NavigateFunction) => {
if (searchValue) {
// Store in local storage for search history
const searchValues =
localStorage.getItem("searchValues")?.split("&&") || [];
const existance = searchValues.includes(searchValue);
if (!existance) {
localStorage.setItem(
"searchValues",
[searchValue, ...searchValues].join(searchValues.length > 0 ? "&&" : "")
);
}
// Navigate to the search page with route parameter
const formattedSearchValue = searchValue.replace(/ /g, "-");
navigate(`/search/${formattedSearchValue}`);
}
};
export const formatAccountNumber = (accountNumber: string) => {
return accountNumber
.replace(/\s/g, "")
.replace(/(\d{4})(?=\d)/g, "$1-")
.replace(/-$/, "");
};
// Safe go back with robust SPA-aware detection and graceful fallback
export const safeGoBack = (navigate: NavigateFunction, fallback: string = "/") => {
// Prefer React-Router/Remix history index when available (SPA navigations)
const state = (window.history && window.history.state) as { idx?: number } | null;
if (state && typeof state.idx === "number" && state.idx > 0) {
window.history.back();
return;
}
// Fallback to same-origin referrer + history length
const referrer = document.referrer;
const sameOrigin =
!!referrer &&
(() => {
try {
return new URL(referrer).origin === window.location.origin;
} catch {
return false;
}
})();
if (sameOrigin && window.history.length > 1) {
window.history.back();
return;
}
// Final fallback: navigate to provided fallback path (default "/")
navigate(fallback);
};