diff --git a/package.json b/package.json index 892f0995..133a6a11 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "app.salona", "private": true, - "version": "2.85.9", + "version": "2.86.0", "type": "module", "scripts": { "dev": "vite --host", diff --git a/src/apps/new-ui/components/MobilePicker/CostPicker.tsx b/src/apps/new-ui/components/MobilePicker/CostPicker.tsx index 191542dd..3c4b5adc 100644 --- a/src/apps/new-ui/components/MobilePicker/CostPicker.tsx +++ b/src/apps/new-ui/components/MobilePicker/CostPicker.tsx @@ -3,268 +3,232 @@ import { Modal } from "@/components/ui/modal"; import { Button } from "@/components/ui/button"; interface Props { - value?: string | null; - onChange: (newValue: string) => void; + value?: string | null; + onChange: (newValue: string) => void; } // تابع تبدیل عدد به حروف فارسی const convertToPersianWords = (num: string): string => { - const numericValue = num.replace(/\D/g, ""); - if (!numericValue) return ""; + const numericValue = num.replace(/\D/g, ""); + if (!numericValue) return ""; - const number = parseInt(numericValue, 10); - if (number === 0) return "صفر"; + const number = parseInt(numericValue, 10); + if (number === 0) return "صفر"; - const units = ["", "هزار", "میلیون", "میلیارد"]; - const ones = ["", "یک", "دو", "سه", "چهار", "پنج", "شش", "هفت", "هشت", "نه"]; - const tens = [ - "", - "ده", - "بیست", - "سی", - "چهل", - "پنجاه", - "شصت", - "هفتاد", - "هشتاد", - "نود", - ]; - const hundreds = [ - "", - "صد", - "دویست", - "سیصد", - "چهارصد", - "پانصد", - "ششصد", - "هفتاد", - "هشتصد", - "نهصد", - ]; - const teens = [ - "ده", - "یازده", - "دوازده", - "سیزده", - "چهارده", - "پانزده", - "شانزده", - "هفده", - "هجده", - "نوزده", - ]; + const units = ["", "هزار", "میلیون", "میلیارد"]; + const ones = ["", "یک", "دو", "سه", "چهار", "پنج", "شش", "هفت", "هشت", "نه"]; + const tens = ["", "ده", "بیست", "سی", "چهل", "پنجاه", "شصت", "هفتاد", "هشتاد", "نود"]; + const hundreds = ["", "صد", "دویست", "سیصد", "چهارصد", "پانصد", "ششصد", "هفتاد", "هشتصد", "نهصد"]; + const teens = ["ده", "یازده", "دوازده", "سیزده", "چهارده", "پانزده", "شانزده", "هفده", "هجده", "نوزده"]; - if (number < 10) return ones[number]; - if (number < 20) return teens[number - 10]; - if (number < 100) { - return ( - tens[Math.floor(number / 10)] + - (number % 10 !== 0 ? " و " + ones[number % 10] : "") - ); - } - if (number < 1000) { - return ( - hundreds[Math.floor(number / 100)] + - (number % 100 !== 0 - ? " و " + convertToPersianWords((number % 100).toString()) - : "") - ); - } - - let result = ""; - let chunkIndex = 0; - let tempNumber = number; - - while (tempNumber > 0) { - const chunk = tempNumber % 1000; - if (chunk !== 0) { - let chunkText = convertToPersianWords(chunk.toString()); - if (chunkIndex > 0) { - chunkText += " " + units[chunkIndex]; - } - result = chunkText + (result ? " و " + result : ""); + if (number < 10) return ones[number]; + if (number < 20) return teens[number - 10]; + if (number < 100) { + return tens[Math.floor(number / 10)] + (number % 10 !== 0 ? " و " + ones[number % 10] : ""); + } + if (number < 1000) { + return ( + hundreds[Math.floor(number / 100)] + + (number % 100 !== 0 ? " و " + convertToPersianWords((number % 100).toString()) : "") + ); } - tempNumber = Math.floor(tempNumber / 1000); - chunkIndex++; - } - return result + " تومان"; + let result = ""; + let chunkIndex = 0; + let tempNumber = number; + + while (tempNumber > 0) { + const chunk = tempNumber % 1000; + if (chunk !== 0) { + let chunkText = convertToPersianWords(chunk.toString()); + if (chunkIndex > 0) { + chunkText += " " + units[chunkIndex]; + } + result = chunkText + (result ? " و " + result : ""); + } + tempNumber = Math.floor(tempNumber / 1000); + chunkIndex++; + } + + return result + " تومان"; }; const CostPicker = ({ value, onChange }: Props) => { - const [modalOpen, setModalOpen] = useState(false); - const [inputValue, setInputValue] = useState(""); - const [persianText, setPersianText] = useState(""); - const [modalPosition, setModalPosition] = useState<"center" | "topKeyboard">( - "center" - ); + const [modalOpen, setModalOpen] = useState(false); + const [inputValue, setInputValue] = useState(""); + const [numericOnly, setNumericOnly] = useState(""); + const [persianText, setPersianText] = useState(""); + const [modalPosition, setModalPosition] = useState<"center" | "topKeyboard">("center"); - const handleInputChange = (e: React.ChangeEvent) => { - const value = e.target.value; - const numericValue = value.replace(/\D/g, ""); - const formattedValue = numericValue.replace(/\B(?=(\d{3})+(?!\d))/g, ","); + const handleInputChange = (e: React.ChangeEvent) => { + const raw = e.target.value; + const digits = raw.replace(/\D/g, ""); - setInputValue(formattedValue); + const trimmed = digits.replace(/^0+/, ""); - if (numericValue) { - setPersianText(convertToPersianWords(numericValue)); - } else { - setPersianText(""); - } - }; - - const onSubmit = () => { - if (inputValue && !isNaN(Number(inputValue.replace(/,/g, "")))) { - onChange(`${inputValue.replace(/,/g, "")}`); - setModalOpen(false); - } - }; - - // مدیریت موقعیت Modal با استفاده از visualViewport - useEffect(() => { - if (!modalOpen) return; - - const updateModalPosition = () => { - if (window.visualViewport) { - const visualViewport = window.visualViewport; - - // اگر ارتفاع viewport کم شده (کیبورد باز شده) - if (visualViewport.height < window.screen.height * 0.7) { - setModalPosition("topKeyboard"); - } else { - setModalPosition("center"); + if (!trimmed) { + setInputValue(""); + setNumericOnly(""); + setPersianText(""); + return; } - } + + const formattedValue = trimmed.replace(/\B(?=(\d{3})+(?!\d))/g, ","); + + setInputValue(formattedValue); + setNumericOnly(trimmed); + + setPersianText(convertToPersianWords(trimmed)); }; - // اضافه کردن event listeners - if (window.visualViewport) { - window.visualViewport.addEventListener("resize", updateModalPosition); - window.visualViewport.addEventListener("scroll", updateModalPosition); - } - - // موقعیت اولیه رو چک کن - updateModalPosition(); - - // Cleanup function - return () => { - if (window.visualViewport) { - window.visualViewport.removeEventListener( - "resize", - updateModalPosition - ); - window.visualViewport.removeEventListener( - "scroll", - updateModalPosition - ); - } + const isValid = (): boolean => { + return numericOnly.length >= 4 && !isNaN(Number(numericOnly)); }; - }, [modalOpen]); - useEffect(() => { - if (modalOpen && value) { - const formattedValue = value.replace(/\B(?=(\d{3})+(?!\d))/g, ","); - setInputValue(formattedValue); - setPersianText(convertToPersianWords(value)); - } else if (modalOpen) { - setInputValue(""); - setPersianText(""); - } - }, [modalOpen, value]); + const onSubmit = () => { + if (!isValid()) return; + onChange(`${numericOnly}`); + setModalOpen(false); + }; - return ( - <> -
setModalOpen(true)} - className={`h-20 w-20 shadow-md rounded-xl border-2 flex flex-col items-center justify-center cursor-pointer ${ - value - ? "border-[#AA00FF66] text-[#AA00FF]" - : "border-[#F7EEFF] text-[#2D0A48]" - }`} - > - {value ? ( - <> -

- {Number(value).toLocaleString()} -

- - ) : ( - <> -

+

-

دلخواه

- - )} -
+ useEffect(() => { + if (!modalOpen) return; - setModalOpen(false)} - className="p-6 rounded-lg w-full max-w-sm" - position={modalPosition} - > -
- {/* عنوان */} -
-

مقدار تومان

-

- مبلغ مورد نظر را وارد کنید -

-
+ const updateModalPosition = () => { + if (window.visualViewport) { + const visualViewport = window.visualViewport; - {/* اینپوت برای وارد کردن مقدار */} -
-
- - - تومان - + if (visualViewport.height < window.screen.height * 0.7) { + setModalPosition("topKeyboard"); + } else { + setModalPosition("center"); + } + } + }; + + if (window.visualViewport) { + window.visualViewport.addEventListener("resize", updateModalPosition); + window.visualViewport.addEventListener("scroll", updateModalPosition); + } + + updateModalPosition(); + + return () => { + if (window.visualViewport) { + window.visualViewport.removeEventListener("resize", updateModalPosition); + window.visualViewport.removeEventListener("scroll", updateModalPosition); + } + }; + }, [modalOpen]); + + useEffect(() => { + if (modalOpen && value) { + // مقدار ورودی رو بدون صفرهای ابتدایی و با فرمت نمایش می‌دیم + const cleaned = value.replace(/\D/g, "").replace(/^0+/, ""); + const formattedValue = cleaned ? cleaned.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : ""; + setInputValue(formattedValue); + setNumericOnly(cleaned); + setPersianText(cleaned ? convertToPersianWords(cleaned) : ""); + } else if (modalOpen) { + setInputValue(""); + setNumericOnly(""); + setPersianText(""); + } + }, [modalOpen, value]); + + return ( + <> +
setModalOpen(true)} + className={`h-20 w-20 shadow-md rounded-xl border-2 flex flex-col items-center justify-center cursor-pointer ${ + value ? "border-[#AA00FF66] text-[#AA00FF]" : "border-[#F7EEFF] text-[#2D0A48]" + }`} + > + {value ? ( + <> +

{Number(value).toLocaleString()}

+ + ) : ( + <> +

+

+

دلخواه

+ + )}
- {/* نمایش مبلغ به حروف */} - {persianText && ( -
-

- به حروف: -
- {persianText} -

-
- )} -
+ setModalOpen(false)} + className="p-6 rounded-lg w-full max-w-sm" + position={modalPosition} + > +
+ {/* عنوان */} +
+

مقدار تومان

+

مبلغ مورد نظر را وارد کنید

+
- {/* دکمه‌های تایید و لغو */} -
- - -
-
-
- - ); + {/* اینپوت برای وارد کردن مقدار */} +
+
+ + + تومان + +
+ + {/* پیام خطا / راهنما */} + {!isValid() && numericOnly.length > 0 && ( +

+ مبلغ باید حداقل ۴ رقم باشد. +

+ )} + + {/* نمایش مبلغ به حروف */} + {persianText && isValid() && ( +
+

+ به حروف: +
+ {persianText} +

+
+ )} +
+ + {/* دکمه‌های تایید و لغو */} +
+ + +
+
+ + + ); }; export default CostPicker; diff --git a/src/apps/new-ui/pages/GiftCardCreator/utils/GiftCard.ts b/src/apps/new-ui/pages/GiftCardCreator/utils/GiftCard.ts index 78c94e4c..7ab5683d 100644 --- a/src/apps/new-ui/pages/GiftCardCreator/utils/GiftCard.ts +++ b/src/apps/new-ui/pages/GiftCardCreator/utils/GiftCard.ts @@ -13,7 +13,7 @@ export const getExpiryDate = (daysToAdd: number) => { /** مقدار نمایش داده شده برای مبلغ */ export const computeDisplayedAmount = (selected: number | "custom" | null | string, customAmount: number | null) => { - if (selected === "custom" && customAmount != null) return (customAmount * 1000).toLocaleString("fa-IR"); + if (selected === "custom" && customAmount != null) return customAmount.toLocaleString("fa-IR"); if (typeof selected === "number") return (selected * 1000).toLocaleString("fa-IR"); return ""; }; diff --git a/src/apps/new-ui/pages/PurchaseSubscription/+components/ProductCard.tsx b/src/apps/new-ui/pages/PurchaseSubscription/+components/ProductCard.tsx index 3f338904..03390a79 100644 --- a/src/apps/new-ui/pages/PurchaseSubscription/+components/ProductCard.tsx +++ b/src/apps/new-ui/pages/PurchaseSubscription/+components/ProductCard.tsx @@ -189,20 +189,6 @@ const ProductCard: React.FC<{
- - {/* نشان انتخاب شده */} - {/* {selected && ( -
- -
- )} */} ); }; diff --git a/src/apps/new-ui/pages/explore/index.tsx b/src/apps/new-ui/pages/explore/index.tsx index 4a3c8cdd..540eb1e1 100644 --- a/src/apps/new-ui/pages/explore/index.tsx +++ b/src/apps/new-ui/pages/explore/index.tsx @@ -70,7 +70,7 @@ const NewExplore = () => {