feat: Implement price info card component and product categories with grid view
- Added `price-info-card.component` to display total amount, tax, and payable amount. - Integrated price masking directive for formatted price display. - Created `categories.component` to list product categories with loading skeletons. - Implemented `grid-view.component` for displaying products in a grid layout. - Enhanced `products.component` to manage category selection and product display. - Introduced new models for product categories and stock responses. - Updated `main.service.ts` to fetch product categories and stock data. - Refactored `POSStore` to manage state for products, categories, and order items. - Added utility functions for price formatting and number normalization. - Included placeholder images for product and logo.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
export * from './currency';
|
||||
export * from './jalali-date.utils';
|
||||
export * from './name';
|
||||
export * from './price-mask.utils';
|
||||
export * from './time';
|
||||
|
||||
@@ -13,7 +13,7 @@ export function fromJalali(jalaliDate: string): Dayjs {
|
||||
}
|
||||
|
||||
export function formatJalali(date: string | number | Date | Dayjs, format = 'YYYY/MM/DD'): string {
|
||||
return toJalali(date).format(format);
|
||||
return toJalali(date).locale('fa').format(format);
|
||||
}
|
||||
|
||||
export function jalaliDiff(
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
export function toArabicDigitsAwareNumber(str: string): string {
|
||||
if (!str) return '';
|
||||
const map: Record<string, string> = {
|
||||
'٠': '0',
|
||||
'١': '1',
|
||||
'٢': '2',
|
||||
'٣': '3',
|
||||
'٤': '4',
|
||||
'٥': '5',
|
||||
'٦': '6',
|
||||
'٧': '7',
|
||||
'٨': '8',
|
||||
'٩': '9',
|
||||
'۰': '0',
|
||||
'۱': '1',
|
||||
'۲': '2',
|
||||
'۳': '3',
|
||||
'۴': '4',
|
||||
'۵': '5',
|
||||
'۶': '6',
|
||||
'۷': '7',
|
||||
'۸': '8',
|
||||
'۹': '9',
|
||||
};
|
||||
return str.replace(/[٠-٩۰-۹]/g, (d) => map[d] ?? d);
|
||||
}
|
||||
|
||||
export function cleanNumericString(raw: string): string {
|
||||
const ascii = toArabicDigitsAwareNumber(raw || '');
|
||||
return ascii.replace(/[^0-9.\-]/g, '');
|
||||
}
|
||||
|
||||
export function normalizeToNumber(raw: number | string | null | undefined): number | null {
|
||||
if (raw === null || raw === undefined) return null;
|
||||
if (typeof raw === 'number') return isFinite(raw) ? raw : null;
|
||||
const cleaned = cleanNumericString(String(raw));
|
||||
if (cleaned === '') return null;
|
||||
const num = Number(cleaned);
|
||||
return isNaN(num) ? null : num;
|
||||
}
|
||||
|
||||
export function formatNumber(
|
||||
value: number,
|
||||
opts?: { locale?: string; useComma?: boolean; fraction?: number },
|
||||
): string {
|
||||
try {
|
||||
const fmtLocale = opts?.useComma ? 'en-US' : (opts?.locale ?? 'fa-IR');
|
||||
return new Intl.NumberFormat(fmtLocale, {
|
||||
maximumFractionDigits: opts?.fraction ?? 0,
|
||||
minimumFractionDigits: 0,
|
||||
useGrouping: true,
|
||||
}).format(value);
|
||||
} catch (e) {
|
||||
return String(value);
|
||||
}
|
||||
}
|
||||
|
||||
export function formatWithCurrency(
|
||||
num: number | string | null,
|
||||
isInputHost: boolean = false,
|
||||
currency: string | null | undefined = 'ریال',
|
||||
opts?: { locale?: string; useComma?: boolean; fraction?: number },
|
||||
): string {
|
||||
if (num === null) return '';
|
||||
const formatted = formatNumber(parseFloat(num + ''), opts);
|
||||
if (isInputHost) return formatted;
|
||||
if (currency && currency.trim()) return `${formatted} ${currency.trim()}`;
|
||||
return formatted;
|
||||
}
|
||||
|
||||
export default {
|
||||
toArabicDigitsAwareNumber,
|
||||
cleanNumericString,
|
||||
normalizeToNumber,
|
||||
formatNumber,
|
||||
formatWithCurrency,
|
||||
};
|
||||
Reference in New Issue
Block a user