feat: unify currency display to toman across the application
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
# واحد پول = تومان در سایت عمومی (نمایش ÷۱۰ / ورودی ×۱۰) — API ریال میماند
|
||||
|
||||
## پروژه
|
||||
|
||||
`nobat724_front` (سایت عمومی). **cross-repo:** پرامپت همتا (ادمین): `clinicpro/.claude/prompt/currency-toman-display-admin.md`. API بکاند (`clinicpro`) واحدش **ریال** است و تغییر نمیکند؛ فیلدهای `*_rials` ریال برمیگردانند.
|
||||
|
||||
## زمینه
|
||||
|
||||
واحد نمایشِ سایت باید **تومان** باشد، اما API همهٔ مبالغ را **ریال** میدهد و میگیرد (`appointment_fee_rials`، `amount_rials`، …) و ارسال به درگاه هم ریال است (بکاند مدیریت میکند). پس فقط باید در **UI** مقدار ریال ÷۱۰ و با «تومان» نشان داده شود، و اگر جایی مبلغ از کاربر گرفته میشود ×۱۰ به ریال تبدیل شود.
|
||||
|
||||
الان ناهماهنگ است: بعضی جاها عددِ `*_rials` خام با برچسب «ریال» نمایش داده میشود (`paying`, transactions, payment result, dashboard balance) و یکجا (`app/dashboard/page.js`) دستی `/10` شده. باید یکدست شود.
|
||||
|
||||
## مشکل / هدف
|
||||
|
||||
همهٔ مبالغِ نمایشی در سایت به **تومان** (÷۱۰ از مقدار ریالِ API) با برچسب «تومان» نشان داده شوند؛ منطق پرداخت/ارسال به درگاه (سمت بکاند، ریال) دست نخورد.
|
||||
|
||||
## فایلهای مرتبط
|
||||
|
||||
| فایل | نقش |
|
||||
|------|-----|
|
||||
| `nobat724_front/lib/money.js` | **جدید** — helper تبدیل/فرمت مرکزی |
|
||||
| `nobat724_front/components/appointment/paying/index.js` | مبلغ نوبت + دکمهٔ پرداخت (ریال→تومان) |
|
||||
| `nobat724_front/components/dashboard/userAccount/sidebars/transactions/Card.js` | مبلغ تراکنش |
|
||||
| `nobat724_front/components/dashboard/userAccount/sidebars/transactions/List.js` | مبلغ تراکنش |
|
||||
| `nobat724_front/components/dashboard/userAccount/Head.js` | برچسب/موجودی |
|
||||
| `nobat724_front/app/payment/[uuid]/page.js` | مبلغ نتیجهٔ پرداخت |
|
||||
| `nobat724_front/app/dashboard/page.js` | جمع تراکنشها (حذف `/10` دستی، استفاده از helper) |
|
||||
|
||||
> با `grep -rn "ریال\|amount_rials\|_rials\|toLocaleString" components app` بقیهٔ نقاط مبلغ را هم پیدا و یکدست کن.
|
||||
|
||||
## وضعیت فعلی
|
||||
|
||||
### `components/appointment/paying/index.js`
|
||||
```js
|
||||
setFeeRials(Number(res?.data?.appointment_fee_rials) || 0);
|
||||
// ...
|
||||
const rials = feeRials != null ? feeRials.toLocaleString("fa-IR") : "—";
|
||||
// ...
|
||||
{rials} <span className="...">ریال</span>
|
||||
// ...
|
||||
{testMode ? "پرداخت آزمایشی" : `پرداخت ${rials} ریال`}
|
||||
```
|
||||
مقدارِ ریالِ خام را با «ریال» نشان میدهد (باید تومان).
|
||||
|
||||
### `app/dashboard/page.js` (تبدیل دستی ناهماهنگ)
|
||||
```js
|
||||
const totalTransactions = Math.round(totalRials / 10); // ریال → تومان
|
||||
```
|
||||
|
||||
### `app/payment/[uuid]/page.js`
|
||||
```js
|
||||
{formatAmount(payment.amount_rials)} ریال
|
||||
```
|
||||
|
||||
## وظایف
|
||||
|
||||
### ۱. helper مرکزی `lib/money.js`
|
||||
|
||||
```js
|
||||
export const RIAL_PER_TOMAN = 10;
|
||||
export const rialToToman = (rial) => Math.round((Number(rial) || 0) / RIAL_PER_TOMAN);
|
||||
export const tomanToRial = (toman) => Math.round((Number(toman) || 0) * RIAL_PER_TOMAN);
|
||||
// فرمت فارسیِ مبلغِ تومان از مقدار ریالِ API
|
||||
export const formatToman = (rial) => rialToToman(rial).toLocaleString("fa-IR");
|
||||
```
|
||||
|
||||
### ۲. `paying/index.js`
|
||||
|
||||
- نمایش مبلغ: `const toman = feeRials != null ? formatToman(feeRials) : "—";`
|
||||
- جای «ریال» → «تومان»؛ دکمه: `پرداخت ${toman} تومان`.
|
||||
- `feeRials` را همچنان از `appointment_fee_rials` بگیر (ریال)؛ فقط نمایش تبدیل شود. **مبلغی که برای شروع پرداخت به بکاند/درگاه میرود تغییر نکند** (بکاند ریال میفرستد).
|
||||
|
||||
### ۳. تراکنشها و موجودی داشبورد
|
||||
|
||||
- `transactions/Card.js` و `List.js`: `Number(data.amount_rials)` خام + «ریال» → `formatToman(data.amount_rials)` + «تومان».
|
||||
- `userAccount/Head.js`: موجودی/برچسب «ریال» → تومان (اگر عددی خام کنارش هست با `formatToman`).
|
||||
- `app/dashboard/page.js`: بهجای `Math.round(totalRials/10)` از `rialToToman(totalRials)` استفاده کن (همان نتیجه، یکدست).
|
||||
|
||||
### ۴. صفحهٔ نتیجهٔ پرداخت
|
||||
|
||||
- `app/payment/[uuid]/page.js`: `{formatAmount(payment.amount_rials)} ریال` → `{formatToman(payment.amount_rials)} تومان` (یا `formatAmount` را با `rialToToman` هماهنگ کن؛ اگر `formatAmount` جای دیگری هم استفاده میشود، دوبارهتبدیل نشود).
|
||||
|
||||
### ۵. جستجوی باقیماندهٔ «ریال»
|
||||
|
||||
`grep -rn "ریال" components app` → هر مبلغِ خام ریال را `formatToman` + «تومان» کن. برچسبهای ثابت «ریال» به «تومان».
|
||||
|
||||
## نکات مهم
|
||||
|
||||
- **API/درگاه ریال میماند:** هیچ فراخوانی `services/response.js` و هیچ مقدارِ ارسالی به بکاند تغییر نمیکند؛ فقط نمایش. مبلغ پرداخت را بکاند (ریال) مدیریت میکند.
|
||||
- **ورودی پول (اگر هست):** اگر جایی کاربر مبلغ وارد میکند که به API میرود، با `tomanToRial` به ریال تبدیل کن (در این سایت معمولاً ورودی پول نیست؛ بیشتر نمایش است).
|
||||
- **double-convert نکن:** جایی که قبلاً `/10` شده (dashboard/page.js) را با helper جایگزین کن، نه اضافه.
|
||||
- **RTL/فونت/Jalali** مثل قبل؛ فقط عدد و برچسب واحد عوض میشود.
|
||||
- تست: `npm run lint` و `npm run build` بدون خطا؛ سپس چشمی صفحهٔ پرداخت نوبت، داشبورد تراکنشها، و نتیجهٔ پرداخت — اعداد باید ۱۰ برابر کوچکتر و با «تومان» باشند.
|
||||
@@ -6,6 +6,7 @@ import { removeToken } from "@/utils";
|
||||
import { cookies } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
import { fetchReq } from "@/lib/req";
|
||||
import { rialToToman } from "@/lib/money";
|
||||
import { safeJsonParse } from "@/lib/sanitize";
|
||||
import { buildPatientUser } from "@/lib/representationAdapters";
|
||||
import { getServerAccessToken } from "@/lib/serverToken";
|
||||
@@ -44,7 +45,7 @@ export default async function Dashboard({ searchParams }) {
|
||||
const totalRials = payments
|
||||
.filter((p) => p.status === "success")
|
||||
.reduce((sum, p) => sum + (Number(p.amount_rials) || 0), 0);
|
||||
const totalTransactions = Math.round(totalRials / 10); // ریال → تومان
|
||||
const totalTransactions = rialToToman(totalRials); // ریال → تومان
|
||||
|
||||
const extras = {
|
||||
mobile: userInfo?.mobile_number ?? "",
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import { request } from "@/services/response";
|
||||
import { formatToman } from "@/lib/money";
|
||||
import Cookies from "js-cookie";
|
||||
import Layout from "@/components/layout";
|
||||
|
||||
@@ -95,9 +96,7 @@ export default function PaymentDetailsPage() {
|
||||
}).format(date);
|
||||
};
|
||||
|
||||
const formatAmount = (rials) => {
|
||||
return new Intl.NumberFormat("fa-IR").format(parseInt(rials));
|
||||
};
|
||||
const formatAmount = (rials) => formatToman(rials);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
@@ -153,7 +152,7 @@ export default function PaymentDetailsPage() {
|
||||
<div className="flex justify-between items-center py-3 border-b border-gray-100">
|
||||
<span className="text-[#616161] font-medium">مبلغ:</span>
|
||||
<span className="text-[#3B3B3B] font-bold text-lg">
|
||||
{formatAmount(payment.amount_rials)} ریال
|
||||
{formatAmount(payment.amount_rials)} تومان
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import ArrowLeftB from "@/components/icons/ArrowLeftB";
|
||||
import { Button, MenuItem, Select, FormControl, InputLabel } from "@mui/material";
|
||||
import ButtonFixed from "./ButtonFixed";
|
||||
import { request } from "@/services/response";
|
||||
import { formatToman } from "@/lib/money";
|
||||
|
||||
const PAYMENT_TTL = 900;
|
||||
|
||||
@@ -44,7 +45,7 @@ function Paying({ setStep, appointmentId, expiresAt, doctor, data, isForAnother,
|
||||
return `${m}:${s.toString().padStart(2, "0")}`;
|
||||
};
|
||||
|
||||
const rials = feeRials != null ? feeRials.toLocaleString("fa-IR") : "—";
|
||||
const toman = feeRials != null ? formatToman(feeRials) : "—";
|
||||
const patientName = isForAnother
|
||||
? [data?.name?.value, data?.family?.value].filter(Boolean).join(" ").trim()
|
||||
: data?.name?.value;
|
||||
@@ -105,7 +106,7 @@ function Paying({ setStep, appointmentId, expiresAt, doctor, data, isForAnother,
|
||||
مبلغ قابل پرداخت
|
||||
</span>
|
||||
<span className="text-[#3B3B3B] text-[18px] md:text-[20px] font-bold">
|
||||
{rials} <span className="text-[14px] font-normal text-[#616161]">ریال</span>
|
||||
{toman} <span className="text-[14px] font-normal text-[#616161]">تومان</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -203,7 +204,7 @@ function Paying({ setStep, appointmentId, expiresAt, doctor, data, isForAnother,
|
||||
}
|
||||
>
|
||||
<span className={`text-[#FFF] text-[16px] font-medium ${loading ? "opacity-0" : ""}`}>
|
||||
{testMode ? "پرداخت آزمایشی" : `پرداخت ${rials} ریال`}
|
||||
{testMode ? "پرداخت آزمایشی" : `پرداخت ${toman} تومان`}
|
||||
</span>
|
||||
{!loading && <ArrowLeftB />}
|
||||
</Button>
|
||||
|
||||
@@ -48,7 +48,7 @@ function Head({ user }) {
|
||||
<div className="flex flex-col gap-[8px] items-start justify-start">
|
||||
<p className="text-[#3B3B3B] text-nowrap text-[16px] font-medium">
|
||||
{Number(user?.total_transactions || 0).toLocaleString("fa-IR")}{" "}
|
||||
<span className="text-[12px]">ریال</span>
|
||||
<span className="text-[12px]">تومان</span>
|
||||
</p>
|
||||
<h1 className="text-[#616161] text-nowrap text-[14px] font-normal">
|
||||
مجموع تراکنش ها
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import TextLoading from "@/app/component/loading/Text";
|
||||
import { convertTimestampToJalali } from "@/helper";
|
||||
import { formatToman } from "@/lib/money";
|
||||
import Link from "next/link";
|
||||
|
||||
const PAYMENT_STATUS = {
|
||||
@@ -62,7 +63,7 @@ function Card({ data, loading }) {
|
||||
</TextLoading>
|
||||
<TextLoading loading={loading} width={80} height={18}>
|
||||
<p className="text-[#616161] text-[14px] font-medium">
|
||||
{data.amount_rials ? Number(data.amount_rials).toLocaleString("fa-IR") : "--"} ریال
|
||||
{data.amount_rials ? formatToman(data.amount_rials) : "--"} تومان
|
||||
</p>
|
||||
</TextLoading>
|
||||
</li>
|
||||
|
||||
@@ -6,6 +6,7 @@ import TextLoading from "@/app/component/loading/Text";
|
||||
import CustomLoading from "@/app/component/loading/Custom";
|
||||
import CircularLoading from "@/app/component/loading/Circular";
|
||||
import { convertTimestampToJalali } from "@/helper";
|
||||
import { formatToman } from "@/lib/money";
|
||||
import Pagination from "@/app/component/Pagination";
|
||||
import Link from "next/link";
|
||||
|
||||
@@ -25,7 +26,7 @@ const TYPE_LABELS = {
|
||||
|
||||
function List({ payments, loading, page, totalPages, onPageChange }) {
|
||||
const rows = Array.isArray(payments) ? payments : [];
|
||||
const tableHead = ["ردیف", "شناسه پرداخت", "نوع", "تاریخ", "مبلغ (ریال)", "وضعیت"];
|
||||
const tableHead = ["ردیف", "شناسه پرداخت", "نوع", "تاریخ", "مبلغ (تومان)", "وضعیت"];
|
||||
const centerTable = [0, 4, 5];
|
||||
|
||||
if (!loading && rows.length === 0) {
|
||||
@@ -80,7 +81,7 @@ function List({ payments, loading, page, totalPages, onPageChange }) {
|
||||
align="right"
|
||||
>
|
||||
<TextLoading width={50} height={18} loading={loading}>
|
||||
{row.amount_rials ? Number(row.amount_rials).toLocaleString("fa-IR") : "--"}
|
||||
{row.amount_rials ? formatToman(row.amount_rials) : "--"}
|
||||
</TextLoading>
|
||||
</TableCell>
|
||||
<TableCell className="!pr-[18px] !text-nowrap" align="right">
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// واحد API/درگاه ریال است؛ نمایش تومان (÷۱۰) و در صورت ورودی ×۱۰.
|
||||
export const RIAL_PER_TOMAN = 10;
|
||||
|
||||
export const rialToToman = (rial) => Math.round((Number(rial) || 0) / RIAL_PER_TOMAN);
|
||||
export const tomanToRial = (toman) => Math.round((Number(toman) || 0) * RIAL_PER_TOMAN);
|
||||
|
||||
// فرمت فارسیِ مبلغ تومان از مقدار ریالِ API.
|
||||
export const formatToman = (rial) => rialToToman(rial).toLocaleString("fa-IR");
|
||||
Reference in New Issue
Block a user