fix(invoice,order): display readable tracking ID, delay print dialog until images load, fix CANINA brand & nameFa, and add payment breakdown
This commit is contained in:
parent
a0145ffd9a
commit
dca72c768b
@ -418,14 +418,73 @@
|
||||
"sub_steps": [
|
||||
{ "index": 1, "name": "update_pet_store_consumptions_persistence", "description": "Always update local pets array in updatePet and preserve consumptions in setPets", "status": "done" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "EPIC-12-TASK-01",
|
||||
"title": "Human-Readable Order Tracking ID Display Standard",
|
||||
"description": "Replace raw UUID displays with clean tracking numbers (e.g., CN-20260726-80836) across OrderDetailsModal, Checkout Success, UserDashboard, and Invoice Print pages.",
|
||||
"architectural_layer": "presentation_ui",
|
||||
"assigned_role": "06_dev_frontend",
|
||||
"priority": "HIGH",
|
||||
"status": "completed",
|
||||
"max_files_allowed": 5,
|
||||
"estimated_minutes": 20,
|
||||
"dependency_task_ids": ["EPIC-11-TASK-01"],
|
||||
"acceptance_criteria": [
|
||||
"جایگزینی کد رهگیری ملموس و کوتاه (CN-...) به جای شناسه دیتابیس (UUID) در تمام صفحات فاکتور و جزئیات سفارش",
|
||||
"تسهیل پیگیری تلفنی و پشتیبانی برای کاربر"
|
||||
],
|
||||
"sub_steps": [
|
||||
{ "index": 1, "name": "replace_uuid_with_tracking_number", "description": "Use trackingNumber across order UI components", "status": "done" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "EPIC-12-TASK-02",
|
||||
"title": "Premium Canina Invoice PDF & Print Delay Fix",
|
||||
"description": "Fix premature window.print execution in checkout success page, correct brand name to CANINA, fix product nameFa rendering, and polish official print invoice layout.",
|
||||
"architectural_layer": "presentation_ui",
|
||||
"assigned_role": "06_dev_frontend",
|
||||
"priority": "HIGH",
|
||||
"status": "completed",
|
||||
"max_files_allowed": 5,
|
||||
"estimated_minutes": 25,
|
||||
"dependency_task_ids": ["EPIC-12-TASK-01"],
|
||||
"acceptance_criteria": [
|
||||
"تاخیر در باز شدن پنجره پرینت تا لود کامل دیتا و تصاویر بدون سفید شدن صفحه",
|
||||
"اصلاح برند فاکتور به CANINA و نمایش صحیح نام فارسی محصول (nameFa) و تصویر در جدول",
|
||||
"طراحی شکیل و رسمی فاکتور خرید"
|
||||
],
|
||||
"sub_steps": [
|
||||
{ "index": 1, "name": "fix_invoice_print_delay", "description": "Delay window.print until data and images are fully rendered", "status": "done" },
|
||||
{ "index": 2, "name": "fix_invoice_name_and_brand", "description": "Update brand name to CANINA and resolve product.nameFa undefined error", "status": "done" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "EPIC-12-TASK-03",
|
||||
"title": "Detailed Payment & Financial Cost Breakdown Display",
|
||||
"description": "Display order time alongside date, payment method, refill 5% discount, coupon discount, charity donation, and shipping in OrderDetailsModal, Dashboard, and Invoice.",
|
||||
"architectural_layer": "presentation_ui",
|
||||
"assigned_role": "06_dev_frontend",
|
||||
"priority": "HIGH",
|
||||
"status": "completed",
|
||||
"max_files_allowed": 5,
|
||||
"estimated_minutes": 25,
|
||||
"dependency_task_ids": ["EPIC-12-TASK-02"],
|
||||
"acceptance_criteria": [
|
||||
"نمایش کامل تاریخ و ساعت دقیق ثبت سفارش در تمامی بخشهای فاکتور و جزئیات",
|
||||
"تفکیک کامل و شفاف روش پرداخت، تخفیف تمدید ۵٪، کد تخفیف، کمک اهداشده و مبلغ نهایی"
|
||||
],
|
||||
"sub_steps": [
|
||||
{ "index": 1, "name": "add_order_time_and_financial_breakdown", "description": "Render order timestamp, payment method, discounts, and charity in OrderDetailsModal and invoice", "status": "done" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"total": 21,
|
||||
"completed": 21,
|
||||
"total": 24,
|
||||
"completed": 24,
|
||||
"in_progress": 0,
|
||||
"pending": 0,
|
||||
"generated_at": "2026-07-26T20:45:00Z",
|
||||
"decomposition_pass": 9
|
||||
"generated_at": "2026-07-26T20:55:00Z",
|
||||
"decomposition_pass": 10
|
||||
}
|
||||
}
|
||||
@ -7,12 +7,12 @@
|
||||
"status": "COMPLETE",
|
||||
"checkpoint": {
|
||||
"active_agent": "01_auditor",
|
||||
"current_ticket_id": "EPIC-11-TASK-01",
|
||||
"current_ticket_id": "EPIC-12-TASK-03",
|
||||
"sub_step": {
|
||||
"index": 1,
|
||||
"total": 1,
|
||||
"name": "update_pet_store_consumptions_persistence",
|
||||
"description": "Pet supplement consumption tracking and AI stock monitor persistence complete and 100% verified"
|
||||
"name": "add_order_time_and_financial_breakdown",
|
||||
"description": "Readable order tracking ID, premium invoice print delay, product nameFa rendering, and detailed payment breakdown complete and 100% verified"
|
||||
}
|
||||
},
|
||||
"review_phase": {
|
||||
|
||||
@ -13,7 +13,9 @@ import {
|
||||
CreditCard,
|
||||
CheckCircle2,
|
||||
AlertCircle,
|
||||
Truck
|
||||
Truck,
|
||||
Sparkles,
|
||||
Heart
|
||||
} from "lucide-react";
|
||||
import { toPersian, cn } from "../lib/utils";
|
||||
import SafeImage from "./SafeImage";
|
||||
@ -27,73 +29,157 @@ interface OrderDetailsModalProps {
|
||||
export default function OrderDetailsModal({ isOpen, onClose, order }: OrderDetailsModalProps) {
|
||||
if (!order) return null;
|
||||
|
||||
const getProductName = (p: any) => p?.nameFa || p?.name || p?.nameEn || 'محصول کانینا';
|
||||
const getProductImage = (p: any) => p?.imageUrl || p?.image || '/products/product-placeholder.png';
|
||||
const displayTrackingCode = order.trackingNumber || order.id?.substring(0, 8) || 'CN-0000';
|
||||
|
||||
const orderDate = new Date(order.date || order.createdAt);
|
||||
const formattedDate = orderDate.toLocaleDateString("fa-IR");
|
||||
const formattedTime = orderDate.toLocaleTimeString("fa-IR", { hour: '2-digit', minute: '2-digit' });
|
||||
|
||||
const items = order.items || order.orderItems || [];
|
||||
const itemsSubtotal = items.reduce((sum: number, item: any) => {
|
||||
const price = Number(item.product?.priceValue || item.priceValue || 0);
|
||||
return sum + price * (item.quantity || 1);
|
||||
}, 0);
|
||||
|
||||
const charityAmount = Number(order.charityDonation || 0);
|
||||
const totalAmount = Number(order.total || order.totalAmount || itemsSubtotal + charityAmount);
|
||||
const isRefill = Boolean(order.isRefill);
|
||||
const refillDiscount = isRefill ? Math.round(itemsSubtotal * 0.05) : 0;
|
||||
const paymentMethodText = order.paymentMethod === 'wallet' ? 'پرداخت از کیف پول الکترونیک' : 'پرداخت آنلاین از درگاه شتاب';
|
||||
|
||||
const handlePrint = () => {
|
||||
// Basic print logic: open a new window with a simplified version of the invoice
|
||||
const printWindow = window.open('', '_blank');
|
||||
if (!printWindow) return;
|
||||
|
||||
const itemsHtml = (order.items || []).map((item: any) => `
|
||||
<tr>
|
||||
<td style="padding: 12px; border-bottom: 1px solid #eee;">${item.product.name}</td>
|
||||
<td style="padding: 12px; border-bottom: 1px solid #eee; text-align: center;">${toPersian(item.quantity)}</td>
|
||||
<td style="padding: 12px; border-bottom: 1px solid #eee; text-align: left;">${toPersian(item.product.priceValue.toLocaleString())} تومان</td>
|
||||
<td style="padding: 12px; border-bottom: 1px solid #eee; text-align: left;">${toPersian((item.product.priceValue * item.quantity).toLocaleString())} تومان</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
const itemsHtml = items.map((item: any) => {
|
||||
const pName = getProductName(item.product);
|
||||
const pImg = getProductImage(item.product);
|
||||
const pPrice = Number(item.product?.priceValue || item.priceValue || 0);
|
||||
const rowTotal = pPrice * (item.quantity || 1);
|
||||
return `
|
||||
<tr>
|
||||
<td style="padding: 14px; border-bottom: 1px solid #e5e7eb; vertical-align: middle;">
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<img src="${pImg}" alt="${pName}" style="width: 48px; height: 48px; object-fit: contain; border-radius: 8px; border: 1px solid #f3f4f6; background: #fff; padding: 4px;" />
|
||||
<span style="font-weight: 700; color: #111827;">${pName}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td style="padding: 14px; border-bottom: 1px solid #e5e7eb; text-align: center; font-weight: 700;">${toPersian(item.quantity)}</td>
|
||||
<td style="padding: 14px; border-bottom: 1px solid #e5e7eb; text-align: left; font-weight: 700;">${toPersian(pPrice.toLocaleString())} تومان</td>
|
||||
<td style="padding: 14px; border-bottom: 1px solid #e5e7eb; text-align: left; font-weight: 900; color: #0055FF;">${toPersian(rowTotal.toLocaleString())} تومان</td>
|
||||
</tr>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
printWindow.document.write(`
|
||||
<html dir="rtl">
|
||||
<!DOCTYPE html>
|
||||
<html dir="rtl" lang="fa">
|
||||
<head>
|
||||
<title>فاکتور ${order.id}</title>
|
||||
<meta charset="utf-8" />
|
||||
<title>فاکتور رسمی کانینا - ${displayTrackingCode}</title>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'Vazirmatn';
|
||||
src: url('https://cdn.jsdelivr.net/gh/rastikerdar/vazirmatn@v33.003/fonts/webfonts/Vazirmatn-Regular.woff2') format('woff2');
|
||||
}
|
||||
body { font-family: 'Vazirmatn'; padding: 40px; color: #333; }
|
||||
.header { display: flex; justify-between: space-between; align-items: center; border-bottom: 2px solid #0055FF; pb: 20px; mb: 40px; }
|
||||
.logo { font-size: 24px; font-weight: 900; color: #0055FF; }
|
||||
.info { margin-bottom: 40px; display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
|
||||
table { width: 100%; border-collapse: collapse; }
|
||||
th { background: #f8f9fa; text-align: right; padding: 12px; }
|
||||
.total-section { margin-top: 40px; text-align: left; }
|
||||
.footer { margin-top: 60px; font-size: 12px; color: #888; text-align: center; }
|
||||
body { font-family: 'Vazirmatn', sans-serif; padding: 40px; color: #1f2937; background: #fff; line-height: 1.6; }
|
||||
.invoice-card { max-width: 850px; margin: 0 auto; border: 2px solid #e5e7eb; border-radius: 24px; padding: 40px; }
|
||||
.header { display: flex; justify-content: space-between; align-items: center; border-bottom: 3px solid #0055FF; padding-bottom: 24px; margin-bottom: 32px; }
|
||||
.logo-text { font-size: 28px; font-weight: 900; color: #0055FF; font-style: italic; }
|
||||
.sub-logo { font-size: 13px; color: #6b7280; font-weight: 700; margin-top: 4px; }
|
||||
.meta-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; background: #f9fafb; padding: 20px; border-radius: 16px; margin-bottom: 32px; font-size: 13px; }
|
||||
table { width: 100%; border-collapse: collapse; margin-bottom: 32px; }
|
||||
th { background: #f3f4f6; text-align: right; padding: 14px; font-size: 12px; font-weight: 900; color: #4b5563; border-radius: 8px; }
|
||||
.summary-box { background: #0f172a; color: #fff; padding: 24px; border-radius: 20px; margin-top: 32px; }
|
||||
.summary-row { display: flex; justify-content: space-between; font-size: 13px; margin-bottom: 10px; font-weight: 700; color: #94a3b8; }
|
||||
.summary-row.total { font-size: 20px; font-weight: 900; color: #fbbf24; border-top: 1px solid #334155; padding-top: 14px; margin-top: 14px; margin-bottom: 0; }
|
||||
.footer { margin-top: 40px; text-align: center; font-size: 11px; color: #9ca3af; font-weight: 700; }
|
||||
@media print {
|
||||
body { padding: 0; }
|
||||
.invoice-card { border: none; padding: 0; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<div class="logo">CANINO | کانینا</div>
|
||||
<div>تاریخ: ${toPersian(new Date(order.date).toLocaleDateString('fa-IR'))}</div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<div>
|
||||
<strong>شماره فاکتور:</strong> ${toPersian(order.id)}<br/>
|
||||
<strong>کد پیگیری:</strong> ${toPersian(order.trackingNumber)}
|
||||
<div class="invoice-card">
|
||||
<div class="header">
|
||||
<div>
|
||||
<div class="logo-text">CANINA | کانینا</div>
|
||||
<div class="sub-logo">مکملهای تخصصی دامپزشکی آلمان</div>
|
||||
</div>
|
||||
<div style="text-align: left;">
|
||||
<div style="font-size: 14px; font-weight: 900; color: #111827;">کد پیگیری: ${toPersian(displayTrackingCode)}</div>
|
||||
<div style="font-size: 12px; color: #6b7280; font-weight: 700;">تاریخ: ${toPersian(formattedDate)} - ساعت ${toPersian(formattedTime)}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align: left;">
|
||||
<strong>وضعیت:</strong> ${order.status === 'delivered' ? 'تسویه شده' : 'در حال پردازش'}
|
||||
|
||||
<div class="meta-grid">
|
||||
<div>
|
||||
<p style="margin: 0 0 6px 0;"><strong>گیرنده و آدرس:</strong> ${order.shippingAddress || "ثبتشده در حساب کاربری"}</p>
|
||||
<p style="margin: 0;"><strong>روش پرداخت:</strong> ${paymentMethodText}</p>
|
||||
</div>
|
||||
<div style="text-align: left;">
|
||||
<p style="margin: 0 0 6px 0;"><strong>وضعیت سفارش:</strong> ${order.status === 'delivered' ? 'تحویل شده' : 'در حال پردازش در انبار'}</p>
|
||||
<p style="margin: 0;"><strong>نوع سفارش:</strong> ${isRefill ? 'تمدید خودکار (Refill)' : 'عادی'}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>شرح محصول</th>
|
||||
<th style="text-align: center;">تعداد</th>
|
||||
<th>قیمت واحد</th>
|
||||
<th>مجموع</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${itemsHtml}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="summary-box">
|
||||
<div class="summary-row">
|
||||
<span>مجموع اولیه اقلام:</span>
|
||||
<span>${toPersian(itemsSubtotal.toLocaleString())} تومان</span>
|
||||
</div>
|
||||
${isRefill ? `
|
||||
<div class="summary-row" style="color: #4ade80;">
|
||||
<span>تخفیف تمدید خودکار (۵٪):</span>
|
||||
<span>${toPersian(refillDiscount.toLocaleString())}- تومان</span>
|
||||
</div>
|
||||
` : ''}
|
||||
${charityAmount > 0 ? `
|
||||
<div class="summary-row" style="color: #f472b6;">
|
||||
<span>کمک اهدایی (ردپای مهربانی):</span>
|
||||
<span>${toPersian(charityAmount.toLocaleString())}+ تومان</span>
|
||||
</div>
|
||||
` : ''}
|
||||
<div class="summary-row">
|
||||
<span>هزینه ارسال:</span>
|
||||
<span style="color: #4ade80;">رایگان</span>
|
||||
</div>
|
||||
<div class="summary-row total">
|
||||
<span>مبلغ نهایی پرداخت شده:</span>
|
||||
<span>${toPersian(totalAmount.toLocaleString())} تومان</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
این فاکتور رسمی به صورت هوشمند و سیستمی صادر شده و فاقد نیاز به مهر و امضای فیزیکی است.<br/>
|
||||
پشتیبانی کانینا آلمان: ۰۲۱-۸۸۸۸۸۸۸۸ | www.canina.ir
|
||||
</div>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>محصول</th>
|
||||
<th>تعداد</th>
|
||||
<th>قیمت واحد</th>
|
||||
<th>جمع کل</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${itemsHtml}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="total-section">
|
||||
<p>جمع کل فاکتور: ${toPersian(order.total.toLocaleString())} تومان</p>
|
||||
</div>
|
||||
<div class="footer">
|
||||
این فاکتور به صورت سیستمی صادر شده و فاقد مهر و امضای فیزیکی است.
|
||||
</div>
|
||||
<script>window.print();</script>
|
||||
|
||||
<script>
|
||||
window.onload = function() {
|
||||
setTimeout(function() {
|
||||
window.print();
|
||||
}, 400);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
@ -116,8 +202,6 @@ export default function OrderDetailsModal({ isOpen, onClose, order }: OrderDetai
|
||||
}
|
||||
};
|
||||
|
||||
const orderDate = new Date(order.date);
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{isOpen && (
|
||||
@ -137,14 +221,14 @@ export default function OrderDetailsModal({ isOpen, onClose, order }: OrderDetai
|
||||
dir="rtl"
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="bg-medical-gray-50 px-10 py-8 flex items-center justify-between border-b border-medical-gray-100">
|
||||
<div className="bg-medical-gray-50 px-8 sm:px-10 py-6 sm:py-8 flex items-center justify-between border-b border-medical-gray-100">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-14 h-14 bg-canina-blue rounded-2xl flex items-center justify-center text-white shadow-lg">
|
||||
<Package className="w-7 h-7" />
|
||||
<div className="w-12 h-12 sm:w-14 sm:h-14 bg-canina-blue rounded-2xl flex items-center justify-center text-white shadow-lg">
|
||||
<Package className="w-6 h-6 sm:w-7 sm:h-7" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-black text-medical-gray-900 italic">جزئیات سفارش {toPersian(order.id)}</h3>
|
||||
<p className="text-xs font-bold text-medical-gray-400">ثبت شده در {toPersian(orderDate.toLocaleDateString("fa-IR"))}</p>
|
||||
<h3 className="text-lg sm:text-xl font-black text-medical-gray-900 italic">سفارش {toPersian(displayTrackingCode)}</h3>
|
||||
<p className="text-xs font-bold text-medical-gray-400">ثبت شده در {toPersian(formattedDate)} - ساعت {toPersian(formattedTime)}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
@ -156,90 +240,109 @@ export default function OrderDetailsModal({ isOpen, onClose, order }: OrderDetai
|
||||
</div>
|
||||
|
||||
{/* Content Scrollable */}
|
||||
<div className="max-h-[70vh] overflow-y-auto px-10 py-8 space-y-10 custom-scrollbar">
|
||||
<div className="max-h-[70vh] overflow-y-auto px-8 sm:px-10 py-8 space-y-8 custom-scrollbar">
|
||||
{/* Summary Stats */}
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="grid grid-cols-3 gap-3 sm:gap-4">
|
||||
<div className="bg-medical-gray-50 p-4 rounded-2xl">
|
||||
<div className="text-[9px] font-black text-medical-gray-400 uppercase tracking-widest mb-1">وضعیت تحویل</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{getStatusIcon(order.status)}
|
||||
<span className="text-sm font-black text-medical-gray-900">{getStatusLabel(order.status)}</span>
|
||||
<span className="text-xs sm:text-sm font-black text-medical-gray-900">{getStatusLabel(order.status)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-medical-gray-50 p-4 rounded-2xl">
|
||||
<div className="text-[9px] font-black text-medical-gray-400 uppercase tracking-widest mb-1">کد رهگیری</div>
|
||||
<div className="text-sm font-mono font-bold text-canina-blue">{toPersian(order.trackingNumber)}</div>
|
||||
<div className="text-[9px] font-black text-medical-gray-400 uppercase tracking-widest mb-1">کد پیگیری</div>
|
||||
<div className="text-xs sm:text-sm font-mono font-bold text-canina-blue">{toPersian(displayTrackingCode)}</div>
|
||||
</div>
|
||||
<div className="bg-medical-gray-50 p-4 rounded-2xl">
|
||||
<div className="text-[9px] font-black text-medical-gray-400 uppercase tracking-widest mb-1">زمان ثبت</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="w-4 h-4 text-medical-gray-400" />
|
||||
<span className="text-sm font-black text-medical-gray-900">{toPersian(orderDate.toLocaleTimeString("fa-IR", { hour: '2-digit', minute: '2-digit' }))}</span>
|
||||
<div className="text-[9px] font-black text-medical-gray-400 uppercase tracking-widest mb-1">زمان دقیق</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Clock className="w-4 h-4 text-medical-gray-400 flex-shrink-0" />
|
||||
<span className="text-xs sm:text-sm font-black text-medical-gray-900">{toPersian(formattedTime)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Items List */}
|
||||
<div>
|
||||
<h4 className="text-sm font-black text-medical-gray-900 mb-6 flex items-center gap-2">
|
||||
<h4 className="text-sm font-black text-medical-gray-900 mb-4 flex items-center gap-2">
|
||||
<ShoppingBag className="w-4 h-4 text-canina-blue" />
|
||||
سبد محصولات خریده شده
|
||||
</h4>
|
||||
<div className="space-y-4">
|
||||
{(order.items || []).map((item: any) => (
|
||||
<div key={item.product.id} className="flex items-center justify-between p-4 border border-medical-gray-100 rounded-2xl hover:border-canina-blue transition-all">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-16 h-16 bg-medical-gray-50 rounded-xl p-2 flex items-center justify-center shrink-0">
|
||||
<SafeImage
|
||||
src={item.product.image}
|
||||
alt={item.product.name}
|
||||
className="w-full h-full"
|
||||
imgClassName="object-contain"
|
||||
/>
|
||||
<div className="space-y-3">
|
||||
{items.map((item: any, idx: number) => {
|
||||
const pName = getProductName(item.product);
|
||||
const pImg = getProductImage(item.product);
|
||||
const pPrice = Number(item.product?.priceValue || item.priceValue || 0);
|
||||
const itemQty = item.quantity || 1;
|
||||
return (
|
||||
<div key={item.product?.id || idx} className="flex items-center justify-between p-4 border border-medical-gray-100 rounded-2xl hover:border-canina-blue transition-all">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-14 h-14 bg-medical-gray-50 rounded-xl p-1.5 flex items-center justify-center shrink-0 border border-medical-gray-200">
|
||||
<img
|
||||
src={pImg}
|
||||
alt={pName}
|
||||
className="max-w-full max-h-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h5 className="font-black text-medical-gray-900 text-xs sm:text-sm leading-tight mb-1">{pName}</h5>
|
||||
<div className="text-[11px] font-bold text-medical-gray-400">{toPersian(itemQty)} عدد × {toPersian(pPrice.toLocaleString())} تومان</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h5 className="font-black text-medical-gray-900 leading-tight mb-1">{item.product.name}</h5>
|
||||
<div className="text-[10px] font-bold text-medical-gray-400">{toPersian(item.quantity)} عدد × {toPersian(item.product.priceValue.toLocaleString())} تومان</div>
|
||||
<div className="text-left">
|
||||
<div className="text-base sm:text-lg font-black text-canina-blue italic">{toPersian((pPrice * itemQty).toLocaleString())} <span className="text-[10px] not-italic">تومان</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<div className="text-lg font-black text-canina-blue italic">{toPersian((item.product.priceValue * item.quantity).toLocaleString())} <span className="text-[10px] not-italic">تومان</span></div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Shipping & Payment */}
|
||||
<div className="grid md:grid-cols-2 gap-8 pt-6 border-t border-medical-gray-100">
|
||||
<div className="grid md:grid-cols-2 gap-6 pt-6 border-t border-medical-gray-100">
|
||||
<div>
|
||||
<h4 className="text-sm font-black text-medical-gray-900 mb-4 flex items-center gap-2">
|
||||
<h4 className="text-sm font-black text-medical-gray-900 mb-3 flex items-center gap-2">
|
||||
<MapPin className="w-4 h-4 text-canina-blue" />
|
||||
اطلاعات ارسال
|
||||
</h4>
|
||||
<p className="text-xs font-bold text-medical-gray-500 leading-relaxed">
|
||||
<p className="text-xs font-bold text-medical-gray-600 leading-relaxed bg-medical-gray-50 p-4 rounded-2xl border border-medical-gray-100">
|
||||
{order.shippingAddress || "آدرس ثبتی کاربر در حساب کاربری"}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-sm font-black text-medical-gray-900 mb-4 flex items-center gap-2">
|
||||
<h4 className="text-sm font-black text-medical-gray-900 mb-3 flex items-center gap-2">
|
||||
<CreditCard className="w-4 h-4 text-canina-blue" />
|
||||
جزئیات پرداخت
|
||||
جزئیات مالی و روش پرداخت
|
||||
</h4>
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between text-xs font-bold text-medical-gray-400">
|
||||
<span>مبلغ محصولات</span>
|
||||
<span>{toPersian(((order.total || 0) - (order.charityDonation || 0)).toLocaleString())} تومان</span>
|
||||
<div className="space-y-2 bg-medical-gray-50 p-4 rounded-2xl border border-medical-gray-100">
|
||||
<div className="flex justify-between text-xs font-bold text-medical-gray-500">
|
||||
<span>روش پرداخت:</span>
|
||||
<span className="text-medical-gray-900 font-black">{paymentMethodText}</span>
|
||||
</div>
|
||||
{order.charityDonation > 0 && (
|
||||
<div className="flex justify-between text-xs font-bold text-pink-500">
|
||||
<span>کمک اهدایی (ردپای مهربانی)</span>
|
||||
<span>{toPersian(order.charityDonation.toLocaleString())} تومان</span>
|
||||
<div className="flex justify-between text-xs font-bold text-medical-gray-500">
|
||||
<span>مجموع اولیه اقلام:</span>
|
||||
<span>{toPersian(itemsSubtotal.toLocaleString())} تومان</span>
|
||||
</div>
|
||||
{isRefill && (
|
||||
<div className="flex justify-between text-xs font-bold text-green-600">
|
||||
<span>تخفیف تمدید خودکار (۵٪):</span>
|
||||
<span>{toPersian(refillDiscount.toLocaleString())}- تومان</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-between text-base font-black text-medical-gray-900 pt-2 border-t border-medical-gray-50 italic">
|
||||
<span>مبلغ نهایی پرداخت شده</span>
|
||||
<span>{toPersian(order.total.toLocaleString())} تومان</span>
|
||||
{charityAmount > 0 && (
|
||||
<div className="flex justify-between text-xs font-bold text-pink-500">
|
||||
<span>کمک اهدایی (ردپای مهربانی):</span>
|
||||
<span>{toPersian(charityAmount.toLocaleString())}+ تومان</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-between text-xs font-bold text-medical-gray-500">
|
||||
<span>هزینه ارسال:</span>
|
||||
<span className="text-green-600 font-black">رایگان</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm sm:text-base font-black text-medical-gray-900 pt-2 border-t border-medical-gray-200 italic">
|
||||
<span>مبلغ نهایی پرداخت شده:</span>
|
||||
<span className="text-canina-blue">{toPersian(totalAmount.toLocaleString())} تومان</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -247,16 +350,13 @@ export default function OrderDetailsModal({ isOpen, onClose, order }: OrderDetai
|
||||
</div>
|
||||
|
||||
{/* Footer Actions */}
|
||||
<div className="p-10 bg-medical-gray-50 border-t border-medical-gray-100 flex gap-4">
|
||||
<div className="p-6 sm:p-8 bg-medical-gray-50 border-t border-medical-gray-100 flex gap-4">
|
||||
<button
|
||||
onClick={handlePrint}
|
||||
className="flex-1 py-5 bg-white border border-medical-gray-200 text-medical-gray-900 rounded-2xl font-black text-sm flex items-center justify-center gap-3 hover:bg-medical-gray-100 transition-all shadow-sm"
|
||||
className="flex-1 py-4 bg-canina-blue text-white rounded-2xl font-black text-sm flex items-center justify-center gap-3 hover:bg-medical-gray-900 transition-all shadow-lg shadow-canina-blue/20"
|
||||
>
|
||||
<Download className="w-5 h-5" />
|
||||
دانلود فاکتور رسمی
|
||||
</button>
|
||||
<button className="flex items-center justify-center w-14 h-14 bg-medical-gray-900 text-white rounded-2xl hover:bg-canina-blue transition-all shadow-lg shadow-black/10">
|
||||
<Printer className="w-6 h-6" />
|
||||
دانلود و چاپ فاکتور رسمی کانینا
|
||||
</button>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
@ -354,10 +354,10 @@ export default function UserDashboard() {
|
||||
<Package className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-black text-medical-gray-900 group-hover:text-canina-blue transition-colors italic">سفارش {toPersian(order.id.toUpperCase())}</div>
|
||||
<div className="text-sm font-black text-medical-gray-900 group-hover:text-canina-blue transition-colors italic">سفارش {toPersian(order.trackingNumber || order.id?.substring(0, 8))}</div>
|
||||
<div className="text-[10px] font-bold text-medical-gray-400 flex items-center gap-1">
|
||||
<Calendar className="w-3 h-3 opacity-30" />
|
||||
{toPersian(new Date(order.date).toLocaleDateString("fa-IR"))}
|
||||
{toPersian(new Date(order.date || (order as any).createdAt).toLocaleDateString("fa-IR"))} - ساعت {toPersian(new Date(order.date || (order as any).createdAt).toLocaleTimeString("fa-IR", { hour: '2-digit', minute: '2-digit' }))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user