Compare commits
2 Commits
aaef1bc11b
...
7b6fe140ad
| Author | SHA1 | Date | |
|---|---|---|---|
| 7b6fe140ad | |||
| 44e2730428 |
@@ -1,6 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { useAppSelector } from "redux/hooks";
|
import { useAppSelector } from "redux/hooks";
|
||||||
import ProText from "../ProText";
|
import ProText from "../ProText";
|
||||||
|
import { formatPriceUi } from "utils/helpers";
|
||||||
|
|
||||||
interface ArabicPriceProps {
|
interface ArabicPriceProps {
|
||||||
price: number | string;
|
price: number | string;
|
||||||
@@ -25,7 +26,8 @@ const ArabicPrice: React.FC<ArabicPriceProps> = ({
|
|||||||
const { restaurant } = useAppSelector((state) => state.order);
|
const { restaurant } = useAppSelector((state) => state.order);
|
||||||
|
|
||||||
// Format the price to ensure it has 2 decimal places
|
// Format the price to ensure it has 2 decimal places
|
||||||
const formattedPrice = typeof price === "number" ? price.toFixed(2) : price;
|
const formattedPrice =
|
||||||
|
typeof price === "number" ? formatPriceUi(price, 3) : price;
|
||||||
const { textDecoration, ...restStyle } = style;
|
const { textDecoration, ...restStyle } = style;
|
||||||
const decorationStyle = textDecoration
|
const decorationStyle = textDecoration
|
||||||
? ({ textDecoration } as React.CSSProperties)
|
? ({ textDecoration } as React.CSSProperties)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import ProInputCard from "../ProInputCard/ProInputCard";
|
|||||||
import styles from "./PaymentMethods.module.css";
|
import styles from "./PaymentMethods.module.css";
|
||||||
import { OrderType } from "pages/checkout/hooks/types.ts";
|
import { OrderType } from "pages/checkout/hooks/types.ts";
|
||||||
import RCardIcon from "components/Icons/RCardIcon";
|
import RCardIcon from "components/Icons/RCardIcon";
|
||||||
|
import { formatPriceUi } from "utils/helpers";
|
||||||
|
|
||||||
const PaymentMethods = () => {
|
const PaymentMethods = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -48,7 +49,7 @@ const PaymentMethods = () => {
|
|||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
value: "cash",
|
value: "cash",
|
||||||
price: grandTotal.toFixed(2),
|
price: formatPriceUi(grandTotal, 3),
|
||||||
style: {
|
style: {
|
||||||
color: colors.primary,
|
color: colors.primary,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -45,13 +45,7 @@ export default function ProductChoicesCard({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Space orientation="vertical" size="small">
|
<Space orientation="vertical" size="small">
|
||||||
<div
|
<div style={{}}>
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
top: 0,
|
|
||||||
[isRTL ? "right" : "left"]: 0,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ProText
|
<ProText
|
||||||
style={{
|
style={{
|
||||||
margin: 0,
|
margin: 0,
|
||||||
@@ -62,7 +56,6 @@ export default function ProductChoicesCard({
|
|||||||
>
|
>
|
||||||
{product.name}
|
{product.name}
|
||||||
</ProText>
|
</ProText>
|
||||||
<br />
|
|
||||||
<ProText
|
<ProText
|
||||||
type="secondary"
|
type="secondary"
|
||||||
className={`${styles.itemDescription} responsive-text`}
|
className={`${styles.itemDescription} responsive-text`}
|
||||||
@@ -92,6 +85,34 @@ export default function ProductChoicesCard({
|
|||||||
{product.type === "OrderItem" && product.variantName}
|
{product.type === "OrderItem" && product.variantName}
|
||||||
</ProText>
|
</ProText>
|
||||||
|
|
||||||
|
{product.extras && (
|
||||||
|
<ProText
|
||||||
|
type="secondary"
|
||||||
|
className={`${styles.itemDescription} responsive-text`}
|
||||||
|
style={{
|
||||||
|
margin: 0,
|
||||||
|
lineClamp: 1,
|
||||||
|
fontSize: isMobile ? 14 : isTablet ? 18 : 20,
|
||||||
|
display: "-webkit-box",
|
||||||
|
WebkitBoxOrient: "vertical",
|
||||||
|
WebkitLineClamp: 1,
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
wordWrap: "break-word",
|
||||||
|
overflowWrap: "break-word",
|
||||||
|
lineHeight: "1.4",
|
||||||
|
maxHeight: isMobile ? "3em" : isTablet ? "5em" : "7em",
|
||||||
|
fontWeight: 500,
|
||||||
|
letterSpacing: "0.01em",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{product.extras.map((o) => (
|
||||||
|
<span key={o.id}>{o.name}</span>
|
||||||
|
))}
|
||||||
|
</ProText>
|
||||||
|
)}
|
||||||
|
|
||||||
{product.type === "CartItem"
|
{product.type === "CartItem"
|
||||||
? product.extrasgroupnew?.map((o) => (
|
? product.extrasgroupnew?.map((o) => (
|
||||||
<ProText
|
<ProText
|
||||||
@@ -122,13 +143,7 @@ export default function ProductChoicesCard({
|
|||||||
: // ToDo
|
: // ToDo
|
||||||
product.itemline}
|
product.itemline}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div style={{}}>
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
bottom: addDividerAfter ? 16 : 3,
|
|
||||||
[isRTL ? "right" : "left"]: 0,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ArabicPrice price={product.price} style={{ fontStyle: "bold" }} />
|
<ArabicPrice price={product.price} style={{ fontStyle: "bold" }} />
|
||||||
</div>
|
</div>
|
||||||
</Space>
|
</Space>
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ export default function PickupEstimateContent({
|
|||||||
let hour = nextHour;
|
let hour = nextHour;
|
||||||
let minute = nextMinute;
|
let minute = nextMinute;
|
||||||
|
|
||||||
for (let i = 1; i < 48; i++) {
|
for (let i = 0; i <= 96; i++) {
|
||||||
const time = dayjs().hour(hour).minute(minute).second(0);
|
const time = dayjs().hour(hour).minute(minute).second(0);
|
||||||
const formatted = time.format("h:mm A");
|
const formatted = time.format("h:mm A");
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Dispatch, SetStateAction } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Extra as ExtraType } from "utils/types/appTypes";
|
import { Extra as ExtraType } from "utils/types/appTypes";
|
||||||
import styles from "../product.module.css";
|
import styles from "../product.module.css";
|
||||||
|
import { formatPriceUi } from "utils/helpers";
|
||||||
|
|
||||||
export default function Extra({
|
export default function Extra({
|
||||||
extrasList,
|
extrasList,
|
||||||
@@ -49,7 +50,7 @@ export default function Extra({
|
|||||||
return {
|
return {
|
||||||
value: value.id.toString(),
|
value: value.id.toString(),
|
||||||
label: value.name,
|
label: value.name,
|
||||||
price: `+${value.price}`,
|
price: `+${formatPriceUi(value.price, 3)}`,
|
||||||
};
|
};
|
||||||
})}
|
})}
|
||||||
value={selectedExtras.map((ex) => ex.id.toString())}
|
value={selectedExtras.map((ex) => ex.id.toString())}
|
||||||
|
|||||||
@@ -94,7 +94,6 @@ export default function ProductFooter({
|
|||||||
const extrasInfo = groupInfo?.extras.filter((e) =>
|
const extrasInfo = groupInfo?.extras.filter((e) =>
|
||||||
selectedGroupExtrasIds.includes(e.id.toString()),
|
selectedGroupExtrasIds.includes(e.id.toString()),
|
||||||
);
|
);
|
||||||
console.log(extrasInfo);
|
|
||||||
return {
|
return {
|
||||||
groupid: key,
|
groupid: key,
|
||||||
extrasid: selectedGroupExtrasIds,
|
extrasid: selectedGroupExtrasIds,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { useAppSelector } from "redux/hooks";
|
import { useAppSelector } from "redux/hooks";
|
||||||
import { Variant } from "utils/types/appTypes";
|
import { Variant } from "utils/types/appTypes";
|
||||||
import styles from "../product.module.css";
|
import styles from "../product.module.css";
|
||||||
|
import { formatPriceUi } from "utils/helpers";
|
||||||
|
|
||||||
export default function Variants({
|
export default function Variants({
|
||||||
selectedVariants,
|
selectedVariants,
|
||||||
@@ -169,7 +170,7 @@ export default function Variants({
|
|||||||
value: value,
|
value: value,
|
||||||
label: value,
|
label: value,
|
||||||
price: variant
|
price: variant
|
||||||
? `+${Number(variant.price).toFixed(2)}`
|
? `+${formatPriceUi(variant.price, 3)}`
|
||||||
: "",
|
: "",
|
||||||
};
|
};
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
ProColumnType,
|
ProColumnType,
|
||||||
RegisterType,
|
RegisterType,
|
||||||
TableHeaderType,
|
TableHeaderType,
|
||||||
Translation
|
Translation,
|
||||||
} from "./types/appTypes";
|
} from "./types/appTypes";
|
||||||
|
|
||||||
export default function getRandomColor() {
|
export default function getRandomColor() {
|
||||||
@@ -25,14 +25,14 @@ export const RGBLinearShade = (p: number, color: string) => {
|
|||||||
|
|
||||||
const P = p < 0 ? 1 + p : 1 - p;
|
const P = p < 0 ? 1 + p : 1 - p;
|
||||||
return `rgb${d ? "a(" : "("}${r(
|
return `rgb${d ? "a(" : "("}${r(
|
||||||
i(a[3] === "a" ? a.slice(5) : a.slice(4)) * P + t
|
i(a[3] === "a" ? a.slice(5) : a.slice(4)) * P + t,
|
||||||
)},${r(i(b) * P + t)},${r(i(c) * P + t)}${d ? `,${d}` : ")"}`;
|
)},${r(i(b) * P + t)},${r(i(c) * P + t)}${d ? `,${d}` : ")"}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const parseTranslationObj = (
|
export const parseTranslationObj = (
|
||||||
prefix: any,
|
prefix: any,
|
||||||
obj: object,
|
obj: object,
|
||||||
form: FormData
|
form: FormData,
|
||||||
) => {
|
) => {
|
||||||
Object.entries(obj).forEach(([key, value]) => {
|
Object.entries(obj).forEach(([key, value]) => {
|
||||||
form.append(`${prefix}[${key}]`, value as string);
|
form.append(`${prefix}[${key}]`, value as string);
|
||||||
@@ -48,8 +48,7 @@ export const parseDataToFormData = (data: any) => {
|
|||||||
Object.entries(data).map((field: any) => {
|
Object.entries(data).map((field: any) => {
|
||||||
const key = field[0];
|
const key = field[0];
|
||||||
const value = field[1];
|
const value = field[1];
|
||||||
if (key === "ar" || key === "en")
|
if (key === "ar" || key === "en") parseTranslationObj(key, value, formData);
|
||||||
parseTranslationObj(key, value, formData);
|
|
||||||
// cleaning (no need to add properties with undefined, null .. etc values)
|
// cleaning (no need to add properties with undefined, null .. etc values)
|
||||||
else if (value || value === 0) formData.append(key, value);
|
else if (value || value === 0) formData.append(key, value);
|
||||||
});
|
});
|
||||||
@@ -82,7 +81,7 @@ export function getArraysDiff(arr1: Array<string>, arr2: Array<string>) {
|
|||||||
|
|
||||||
export function translatedColumnTitle(
|
export function translatedColumnTitle(
|
||||||
title: string | { key: string; varKey: string },
|
title: string | { key: string; varKey: string },
|
||||||
t2: any
|
t2: any,
|
||||||
): string {
|
): string {
|
||||||
if (title && typeof title === "string") return t2(title);
|
if (title && typeof title === "string") return t2(title);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -94,7 +93,7 @@ export function translatedColumnTitle(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getModifiedColProps(
|
export function getModifiedColProps(
|
||||||
col: TableHeaderType | Partial<ProColumnType<any>>
|
col: TableHeaderType | Partial<ProColumnType<any>>,
|
||||||
): Partial<ProColumnType<any>> {
|
): Partial<ProColumnType<any>> {
|
||||||
const translatedTitle = translatedColumnTitle(col.title || "", t);
|
const translatedTitle = translatedColumnTitle(col.title || "", t);
|
||||||
let calcColumnWidth = col.width;
|
let calcColumnWidth = col.width;
|
||||||
@@ -148,7 +147,7 @@ export function prepareNextKey(dataSource: any[]): number {
|
|||||||
export function objectArrayToFormData(
|
export function objectArrayToFormData(
|
||||||
arr: Array<Record<string, any>>,
|
arr: Array<Record<string, any>>,
|
||||||
parentKey?: string,
|
parentKey?: string,
|
||||||
formData?: FormData
|
formData?: FormData,
|
||||||
): FormData {
|
): FormData {
|
||||||
const form = formData || new FormData();
|
const form = formData || new FormData();
|
||||||
|
|
||||||
@@ -213,11 +212,9 @@ export const formatNumbers = (value: string | number = 0) => {
|
|||||||
*/
|
*/
|
||||||
export const parseTranslations = (data: any) => {
|
export const parseTranslations = (data: any) => {
|
||||||
return {
|
return {
|
||||||
arabic_name: data?.translations?.find(
|
arabic_name: data?.translations?.find((t: Translation) => t.locale === "ar")
|
||||||
(t: Translation) => t.locale === "ar"
|
|
||||||
)?.name,
|
|
||||||
name: data?.translations?.find((t: Translation) => t.locale === "en")
|
|
||||||
?.name,
|
?.name,
|
||||||
|
name: data?.translations?.find((t: Translation) => t.locale === "en")?.name,
|
||||||
en: {
|
en: {
|
||||||
name: data?.translations?.find((t: Translation) => t.locale === "en")
|
name: data?.translations?.find((t: Translation) => t.locale === "en")
|
||||||
?.name,
|
?.name,
|
||||||
@@ -238,14 +235,14 @@ export const parseTranslations = (data: any) => {
|
|||||||
export const parseTranslationLabel = (
|
export const parseTranslationLabel = (
|
||||||
data: any,
|
data: any,
|
||||||
propName: string,
|
propName: string,
|
||||||
translationPropName?: string
|
translationPropName?: string,
|
||||||
) => {
|
) => {
|
||||||
return {
|
return {
|
||||||
["arabic_" + propName]: data?.translations?.find(
|
["arabic_" + propName]: data?.translations?.find(
|
||||||
(t: Translation) => t.locale === "ar"
|
(t: Translation) => t.locale === "ar",
|
||||||
)?.[translationPropName || "name"],
|
)?.[translationPropName || "name"],
|
||||||
[propName]: data?.translations?.find(
|
[propName]: data?.translations?.find(
|
||||||
(t: Translation) => t.locale === "en"
|
(t: Translation) => t.locale === "en",
|
||||||
)?.[translationPropName || "name"],
|
)?.[translationPropName || "name"],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -260,7 +257,7 @@ export const saveTranslationsParse = (
|
|||||||
selectedRecord: any,
|
selectedRecord: any,
|
||||||
dataIndex: string,
|
dataIndex: string,
|
||||||
lang: "ar" | "en",
|
lang: "ar" | "en",
|
||||||
value: string
|
value: string,
|
||||||
) => {
|
) => {
|
||||||
return lang === "ar"
|
return lang === "ar"
|
||||||
? {
|
? {
|
||||||
@@ -274,3 +271,126 @@ export const saveTranslationsParse = (
|
|||||||
export const fixNumbers = (number: string) => {
|
export const fixNumbers = (number: string) => {
|
||||||
return number.slice(0, -3);
|
return number.slice(0, -3);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rounds a number if it contains '9' in the decimal part
|
||||||
|
* If '9' is found, rounds to keep the number up to the first '9' plus one more digit
|
||||||
|
* Otherwise returns the number as is
|
||||||
|
* @param number - The number to round
|
||||||
|
* @returns The rounded number or original number
|
||||||
|
*/
|
||||||
|
export const roundNumberIfNeededUi = (number: number): number => {
|
||||||
|
const numberString = number.toString();
|
||||||
|
const decimalIndex = numberString.indexOf(".");
|
||||||
|
|
||||||
|
if (decimalIndex === -1) {
|
||||||
|
return number; // No decimal point, return as is
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there's a '9' in the decimal part
|
||||||
|
let foundNine = false;
|
||||||
|
for (let i = decimalIndex + 1; i < numberString.length; i++) {
|
||||||
|
if (numberString[i] === "9") {
|
||||||
|
foundNine = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundNine) {
|
||||||
|
// If '9' is found, round to 2 decimal places (keeping one 9 after decimal point)
|
||||||
|
// The Dart code uses decimalIndex + 2, which means 2 decimal places total
|
||||||
|
const decimalPlaces = 2;
|
||||||
|
return parseFloat(number.toFixed(decimalPlaces));
|
||||||
|
}
|
||||||
|
|
||||||
|
return number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a price value to have a specific number of decimal places
|
||||||
|
* Pads with zeros if needed, or truncates if too long
|
||||||
|
* @param value - The price value to format
|
||||||
|
* @param currencyDecimals - Number of decimal places (default: 3)
|
||||||
|
* @returns Formatted price string
|
||||||
|
*/
|
||||||
|
export const formatPriceUi = (
|
||||||
|
value: number,
|
||||||
|
currencyDecimals: number = 3,
|
||||||
|
): string => {
|
||||||
|
const valueString = value.toString();
|
||||||
|
const parts = valueString.split(".");
|
||||||
|
const integerPart = parts[0];
|
||||||
|
let decimalPart = parts.length > 1 ? parts[1] : "";
|
||||||
|
|
||||||
|
// Pad or truncate decimal part
|
||||||
|
if (decimalPart.length < currencyDecimals) {
|
||||||
|
decimalPart = decimalPart.padEnd(currencyDecimals, "0");
|
||||||
|
} else if (decimalPart.length > currencyDecimals) {
|
||||||
|
decimalPart = decimalPart.substring(0, currencyDecimals);
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${integerPart}.${decimalPart}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a price for UI display
|
||||||
|
* Handles scientific notation, NaN, and special cases with '9999999' pattern
|
||||||
|
* @param value - The value to format (number or string)
|
||||||
|
* @param currencyDecimals - Number of decimal places (default: 3)
|
||||||
|
* @returns Formatted price string
|
||||||
|
*/
|
||||||
|
export const setPriceUi = (
|
||||||
|
value: number | string,
|
||||||
|
currencyDecimals: number = 3,
|
||||||
|
): string => {
|
||||||
|
// Convert to number with 6 decimal places
|
||||||
|
const numValue =
|
||||||
|
typeof value === "string"
|
||||||
|
? parseFloat(parseFloat(value).toFixed(6)) || 0.0
|
||||||
|
: parseFloat(value.toFixed(6)) || 0.0;
|
||||||
|
|
||||||
|
const valueStr = numValue.toString();
|
||||||
|
|
||||||
|
// Handle scientific notation and NaN
|
||||||
|
if (valueStr.includes("e") || valueStr === "NaN") {
|
||||||
|
return "0.000";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle '9999999' pattern
|
||||||
|
if (valueStr.includes("9999999")) {
|
||||||
|
return roundNumberIfNeededUi(numValue).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatPriceUi(numValue, currencyDecimals);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a price for payment UI display
|
||||||
|
* Handles scientific notation and special cases with '9999999' pattern
|
||||||
|
* @param value - The value to format (number or string)
|
||||||
|
* @param currencyDecimals - Number of decimal places (default: 3)
|
||||||
|
* @returns Formatted price string
|
||||||
|
*/
|
||||||
|
export const setPricePaymentUi = (
|
||||||
|
value: number | string,
|
||||||
|
currencyDecimals: number = 3,
|
||||||
|
): string => {
|
||||||
|
const valueStr = value.toString();
|
||||||
|
|
||||||
|
// Handle scientific notation
|
||||||
|
if (valueStr.includes("e")) {
|
||||||
|
return "0.000";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle '9999999' pattern
|
||||||
|
if (valueStr.includes("9999999")) {
|
||||||
|
const fixed4 =
|
||||||
|
typeof value === "number"
|
||||||
|
? value.toFixed(4)
|
||||||
|
: parseFloat(value).toFixed(4);
|
||||||
|
return fixed4.substring(0, fixed4.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const numValue = typeof value === "string" ? parseFloat(value) : value;
|
||||||
|
return formatPriceUi(numValue, currencyDecimals);
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user