import { clsx, type ClassValue } from "clsx"; import EventEmitter from "events"; import moment from "moment-jalaali"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } export const formatNumber = (value: string | number) => { // if (!value) return "0"; const numericValue = typeof value === "string" ? parseFloat(value) : value; if (isNaN(numericValue)) return "0"; return new Intl.NumberFormat("fa-IR").format(numericValue); }; export const formatCurrency = (amount: string | number) => { if (!amount) return "0 ریال"; // Remove commas and convert to number const numericAmount = typeof amount === "string" ? parseFloat(amount.replace(/,/g, "")) : amount; if (isNaN(numericAmount)) return "0 ریال"; return new Intl.NumberFormat("fa-IR").format(numericAmount) + " ریال"; }; /** * محاسبه دامنه nice numbers برای محور Y نمودارها * @param values آرایه از مقادیر داده‌ها * @param minValue حداقل مقدار (پیش‌فرض: 0 برای داده‌های درصدی) * @param marginPercent درصد حاشیه اضافی (پیش‌فرض: 5%) * @returns شیء شامل حداکثر nice، فاصله tick ها، و آرایه tick ها */ export function calculateNiceRange( values: number[], minValue: number = 0, marginPercent: number = 5 ): { niceMax: number; tickInterval: number; ticks: number[]; } { if (values.length === 0) { return { niceMax: 100, tickInterval: 20, ticks: [0, 20, 40, 60, 80, 100] }; } // پیدا کردن حداکثر مقدار در داده‌ها const dataMax = Math.max(...values); // اگر همه مقادیر صفر یا منفی هستند if (dataMax <= 0) { return { niceMax: 100, tickInterval: 20, ticks: [0, 20, 40, 60, 80, 100] }; } // اضافه کردن حاشیه const maxWithMargin = dataMax * (1 + marginPercent / 100); // محاسبه nice upper limit const niceMax = calculateNiceNumber(maxWithMargin, true); // محاسبه فاصله مناسب tick ها بر اساس niceMax const range = niceMax - minValue; const targetTicks = 5; // هدف: 5 tick const roughTickInterval = range / (targetTicks - 1); const niceTickInterval = calculateNiceNumber(roughTickInterval, false); // ایجاد آرایه tick ها const ticks: number[] = []; for (let i = minValue; i <= niceMax; i += niceTickInterval) { ticks.push(Math.round(i)); } // اطمینان از اینکه niceMax در آرایه tick ها باشد if (ticks[ticks.length - 1] !== niceMax) { ticks.push(niceMax); } return { niceMax, tickInterval: niceTickInterval, ticks, }; } /** * محاسبه عدد nice (گرد و خوانا) بر اساس الگوریتم nice numbers * @param value مقدار ورودی * @param round آیا به سمت بالا گرد شود یا نه * @returns عدد nice */ function calculateNiceNumber(value: number, round: boolean): number { if (value <= 0) return 0; // پیدا کردن قدرت 10 const exponent = Math.floor(Math.log10(value)); const fraction = value / Math.pow(10, exponent); let niceFraction: number; if (round) { // برای حداکثر: به سمت بالا گرد می‌کنیم با دقت بیشتر if (fraction <= 1.0) niceFraction = 1; else if (fraction <= 2.0) niceFraction = 2; else if (fraction <= 2.5) niceFraction = 2.5; else if (fraction <= 5.0) niceFraction = 5; else if (fraction <= 7.5) niceFraction = 7.5; else niceFraction = 10; } else { // برای فاصله tick ها: اعداد ساده‌تر if (fraction <= 1.0) niceFraction = 1; else if (fraction <= 2.0) niceFraction = 2; else if (fraction <= 5.0) niceFraction = 5; else niceFraction = 10; } return niceFraction * Math.pow(10, exponent); } export const handleDataValue = (val: any): any => { moment.loadPersian({ usePersianDigits: true }); if (val == null) return val; if ( typeof val === "string" && /^\d{4}[-/]\d{2}[-/]\d{2}( \d{2}:\d{2}(:\d{2})?)?$/.test(val) ) { return moment(val, "YYYY-MM-DD HH:mm:ss").format("YYYY/MM/DD"); } if ( typeof val === "number" || (typeof val === "string" && /^-?\d+$/.test(val)) ) { return val.toString().replace(/\d/g, (d) => "۰۱۲۳۴۵۶۷۸۹"[+d]); } return val; }; export const EventBus = new EventEmitter();