Compare commits

...

2 Commits

Author SHA1 Message Date
7b6fe140ad increase time & show extra 2026-01-14 22:53:11 +03:00
44e2730428 fix price format (default oman price) 2026-01-14 22:33:07 +03:00
8 changed files with 179 additions and 40 deletions

View File

@@ -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)

View File

@@ -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,
}, },

View File

@@ -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>

View File

@@ -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");

View File

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

View File

@@ -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,

View File

@@ -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)}`
: "", : "",
}; };
})} })}

View File

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