Compare commits

..

No commits in common. "79c72517c295e77e3614b026b98359fdb09c6265" and "8852f6258043926ced4fd8369f8d175965baa98f" have entirely different histories.

6 changed files with 36 additions and 83 deletions

View File

@ -155,25 +155,25 @@ export default function Products() {
const [formData, setFormData] = useState({
artNo: '',
buyPrice: '' as number | string,
priceValue: '' as number | string,
wholesalePrice: '' as number | string,
priceValueMarginPercent: '' as number | string,
wholesaleMarginPercent: '' as number | string,
priceDisplay: '',
unit: '',
packageSize: '' as number | string,
dosageLogic: '',
suitableFor: 'سگ و گربه',
imageUrl: '',
metaTitle: '',
metaDescription: '',
nameFa: '',
nameEn: '',
scientificTagline: '',
description: '',
shortDescription: '',
categoryId: '',
buyPrice: '' as number | string,
priceValue: 0,
wholesalePrice: '' as number | string,
priceValueMarginPercent: '' as number | string,
wholesaleMarginPercent: '' as number | string,
priceDisplay: '',
unit: '',
packageSize: 0,
dosageLogic: '',
suitableFor: 'سگ و گربه',
imageUrl: '',
metaTitle: '',
metaDescription: '',
keywords: '',
canonicalUrl: '',
slug: '',
@ -195,24 +195,17 @@ export default function Products() {
preorderDeposit: '' as number | string
});
const openModal = (product?: Product) => {
const openModal = (product: Product | null = null) => {
setMediaImageError(false);
if (product) {
setEditingProduct(product);
updateUrlParams({ modal: 'edit', productId: product.id, tab: activeTab || 'general' });
const bPrice = product.buyPrice ? Number(product.buyPrice) : '';
const pPrice = product.priceValue !== undefined && product.priceValue !== null ? Number(product.priceValue) : '';
const wPrice = product.wholesalePrice !== undefined && product.wholesalePrice !== null ? Number(product.wholesalePrice) : '';
const pPrice = Number(product.priceValue || 0);
const wPrice = product.wholesalePrice ? Number(product.wholesalePrice) : '';
let pMargin: number | string = '';
let wMargin: number | string = '';
if (bPrice && pPrice !== '') {
pMargin = Math.round(((Number(pPrice) - Number(bPrice)) / Number(bPrice)) * 100);
}
if (bPrice && wPrice !== '') {
wMargin = Math.round(((Number(wPrice) - Number(bPrice)) / Number(bPrice)) * 100);
}
const pMargin = bPrice && pPrice ? Math.round(((pPrice - Number(bPrice)) / Number(bPrice)) * 100) : '';
const wMargin = bPrice && wPrice ? Math.round(((wPrice - Number(bPrice)) / Number(bPrice)) * 100) : '';
setFormData({
artNo: product.artNo || '',
@ -221,7 +214,7 @@ export default function Products() {
scientificTagline: product.scientificTagline || '',
description: product.description || '',
shortDescription: product.shortDescription || '',
categoryId: typeof product.category === 'object' && product.category !== null ? product.category.id : (product.categoryId || ''),
categoryId: product.categoryId || '',
buyPrice: bPrice,
priceValue: pPrice,
wholesalePrice: wPrice,
@ -229,7 +222,7 @@ export default function Products() {
wholesaleMarginPercent: wMargin,
priceDisplay: product.priceDisplay || '',
unit: product.unit || '',
packageSize: product.packageSize !== undefined && product.packageSize !== null ? Number(product.packageSize) : '',
packageSize: Number(product.packageSize) || 0,
dosageLogic: product.dosageLogic || '',
suitableFor: product.suitableFor || 'سگ و گربه',
imageUrl: product.imageUrl || '',
@ -253,15 +246,15 @@ export default function Products() {
pdfCover: product.pdfCover || '',
symptoms: product.symptoms ? product.symptoms.map((s: { symptom: string } | string) => typeof s === 'string' ? s : s.symptom) : [],
isPreorder: Boolean(product.isPreorder),
preorderDeposit: product.preorderDeposit !== undefined && product.preorderDeposit !== null ? product.preorderDeposit : ''
preorderDeposit: product.preorderDeposit || ''
});
} else {
setEditingProduct(null);
updateUrlParams({ modal: 'create', productId: undefined, tab: activeTab || 'general' });
setFormData({
artNo: '', nameFa: '', nameEn: '', scientificTagline: '', description: '', shortDescription: '', categoryId: '',
buyPrice: '', priceValue: '', wholesalePrice: '', priceValueMarginPercent: '', wholesaleMarginPercent: '',
priceDisplay: '', unit: '', packageSize: '', dosageLogic: '', suitableFor: 'سگ و گربه',
buyPrice: '', priceValue: 0, wholesalePrice: '', priceValueMarginPercent: '', wholesaleMarginPercent: '',
priceDisplay: '', unit: '', packageSize: 0, dosageLogic: '', suitableFor: 'سگ و گربه',
imageUrl: '', metaTitle: '', metaDescription: '', keywords: '', canonicalUrl: '', slug: '',
images: [],
podcastUrl: '', podcastTitle: '', podcastDescription: '', podcastCover: '',
@ -1118,16 +1111,7 @@ export default function Products() {
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 pt-2">
<div className="space-y-2">
<label className="text-sm font-bold text-gray-700">موجودی *</label>
<input
required
type="number"
min="0"
placeholder="0"
value={formData.packageSize === '' ? '' : formData.packageSize}
onChange={(e) => setFormData({ ...formData, packageSize: e.target.value === '' ? '' : Number(e.target.value) })}
className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-left font-mono font-bold"
dir="ltr"
/>
<input required type="number" min="0" value={formData.packageSize} onChange={(e) => setFormData({ ...formData, packageSize: Number(e.target.value) })} className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none" dir="ltr" />
</div>
<div className="space-y-2">
<label className="text-sm font-bold text-gray-700">واحد سنجش</label>

View File

@ -178,18 +178,7 @@ export default function ZibalPortalPage() {
try {
setLoadingQueue(true);
const res = await api.get('/payment/admin/checkout-queue');
let items: any[] = [];
if (Array.isArray(res.data)) {
items = res.data;
} else if (res.data?.data && Array.isArray(res.data.data)) {
items = res.data.data;
} else if (res.data?.queue && Array.isArray(res.data.queue)) {
items = res.data.queue;
} else if (res.data?.checkouts && Array.isArray(res.data.checkouts)) {
items = res.data.checkouts;
} else if (res.data?.checkoutList && Array.isArray(res.data.checkoutList)) {
items = res.data.checkoutList;
}
const items = Array.isArray(res.data) ? res.data : (res.data?.data || []);
setQueueItems(items);
} catch (e: any) {
toast.error('خطا در دریافت صف تسویه‌های در انتظار');

View File

@ -170,11 +170,7 @@ const ArchiveProductCard: React.FC<{ product: Product }> = ({ product }) => {
) : (
<span className="text-xs font-bold text-amber-600 bg-amber-50 px-2 py-1 rounded-md">تماس جهت استعلام قیمت</span>
)}
{(product.packageSize !== undefined && product.packageSize !== null ? Number(product.packageSize) : 100) <= 0 ? (
<span className="px-2.5 py-1.5 rounded-xl bg-rose-50 text-rose-600 border border-rose-100 text-[11px] font-bold font-vazir">
ناموجود
</span>
) : !useSettingsStore.getState().getText("catalog_disable_cart", "false").includes("true") && (() => {
{!useSettingsStore.getState().getText("catalog_disable_cart", "false").includes("true") && (() => {
const cartItem = useCartStore.getState().items.find(i => i.product.id === product.id);
if (cartItem && cartItem.quantity > 0) {
return (

View File

@ -85,7 +85,7 @@ function ProductCard({ product }: { product: Product }) {
>
<Eye className="w-5 h-5" />
</div>
{allowCart && (product.packageSize !== undefined && product.packageSize !== null ? Number(product.packageSize) : 100) > 0 && (
{allowCart && (
<div
onClick={(e) => {
e.stopPropagation();

View File

@ -143,7 +143,7 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
return {
...apiProduct,
packageSize: apiProduct.packageSize !== undefined && apiProduct.packageSize !== null ? Number(apiProduct.packageSize) : 100,
packageSize: apiProduct.packageSize || 100,
};
}, [product, allProducts]);
@ -185,8 +185,8 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
dailyDose = Math.max(1, Math.round(weight * factor));
}
const packageSize = fullProduct.packageSize !== undefined && fullProduct.packageSize !== null ? Number(fullProduct.packageSize) : 100;
const duration = Math.max(1, Math.floor((packageSize || 100) / (dailyDose || 1)));
const packageSize = fullProduct.packageSize || 100;
const duration = Math.max(1, Math.floor(packageSize / (dailyDose || 1)));
return {
dailyDose,
@ -220,8 +220,6 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
);
if (!product || !fullProduct || !calculation) return <div className="min-h-screen flex items-center justify-center">محصول یافت نشد</div>;
const isOutOfStock = (product.packageSize !== undefined && product.packageSize !== null ? Number(product.packageSize) : 100) <= 0;
return (
<div className="min-h-screen bg-medical-gray-50 pt-2 pb-36 sm:pb-20 px-4 sm:px-6 lg:px-8 font-vazir" dir="rtl">
<div className="max-w-7xl mx-auto w-full">
@ -276,16 +274,10 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
title="کلیک برای مشاهده تصویر بزرگتر"
>
<div className="absolute top-4 left-4 sm:top-6 sm:left-6 flex flex-col gap-1.5 sm:gap-2 z-10 items-start">
{isOutOfStock ? (
<div className="px-2.5 py-1 bg-rose-500 text-white rounded-full text-[9px] sm:text-[10px] font-black uppercase tracking-widest flex items-center gap-1 font-vazir shadow-sm">
ناموجود در انبار
</div>
) : (
<div className="px-2.5 py-1 bg-green-500 text-white rounded-full text-[9px] sm:text-[10px] font-black uppercase tracking-widest flex items-center gap-1 font-vazir shadow-sm">
<CheckCircle2 className="w-3 h-3" />
موجود در انبار
</div>
)}
<div className="px-2.5 py-1 bg-green-500 text-white rounded-full text-[9px] sm:text-[10px] font-black uppercase tracking-widest flex items-center gap-1 font-vazir shadow-sm">
<CheckCircle2 className="w-3 h-3" />
موجود در انبار
</div>
<div className="px-2.5 py-1 bg-canina-blue text-white rounded-full text-[9px] sm:text-[10px] font-black uppercase tracking-widest font-vazir whitespace-nowrap shadow-sm">
گرید دارویی اختصاصی
</div>
@ -1068,14 +1060,6 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
<Phone className="w-4 h-4" />
استعلام و مشاوره
</a>
) : isOutOfStock ? (
<button
onClick={() => toast.info("درخواست اطلاع‌رسانی موجودی ثبت شد")}
className="flex-1 bg-amber-500 text-white py-2.5 px-3 rounded-xl font-black text-xs hover:bg-amber-600 transition-all flex items-center justify-center gap-1.5 shadow-md shadow-amber-500/20 whitespace-nowrap"
>
<Bell className="w-4 h-4" />
موجود شد خبر بده
</button>
) : (
<>
<div className="flex items-center gap-1 bg-medical-gray-50 border border-medical-gray-200 rounded-xl p-1 h-11">

View File

@ -124,7 +124,7 @@ export class ProductService {
category: categoryName || '',
categorySlug: data.categorySlug || '',
unit: data.unit || 'عدد',
packageSize: data.packageSize !== undefined && data.packageSize !== null ? Number(data.packageSize) : 100,
packageSize: Number(data.packageSize || 100),
dosage_logic: data.dosageLogic || '',
benefits: data.benefits || '',
suitableFor: (data.suitableFor as PetType) || 'سگ',