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(); 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); };