From 9310062616f0e1f9981323d9556758775c54ee3d Mon Sep 17 00:00:00 2001 From: payacom <59262811+payacom@users.noreply.github.com> Date: Tue, 15 Sep 2026 17:29:24 +0330 Subject: [PATCH] Fix shop UI RTL/layout consistency and tab reselect behavior - Align shop header layout (logo, name, verification badge, share button) and RTL/LTR positioning across shop, listing, billboard, wallet, gift, and academy pages with the Android app's equivalent screens - Fix TabNavigation's tab-reselect scroll-to-top to only fire on the exact tab route, not any active sub-route - Add verification badge to shop owner name; add brand icon assets Co-Authored-By: Claude Sonnet 5 --- public/images/icons/brand/instagram-brand.svg | 1 + public/images/icons/brand/phone-brand.svg | 14 +++ public/images/icons/brand/telegram-brand.svg | 16 +++ public/images/icons/brand/whatsapp-brand.svg | 26 +++++ .../academy/[id]/[title]/CourseClient.tsx | 8 +- .../academy/payment/failed/page.tsx | 4 +- src/app/billboards/new/[id]/page.tsx | 4 +- src/app/billboards/payment/failed/page.tsx | 8 +- src/app/gift/[userId]/page.tsx | 8 +- src/app/offer/[id]/page.tsx | 13 ++- src/app/settings/shop/list/page.tsx | 21 ++-- src/app/settings/wallet/page.tsx | 7 +- src/app/shops/[shopId]/page.tsx | 87 +++++++++------ .../[shopId]/products/[listingId]/page.tsx | 81 ++++++++++---- .../shops/[shopId]/products/new/name/page.tsx | 6 +- .../[shopId]/products/new/variants/page.tsx | 1 + src/app/shops/checkout/[orderId]/page.tsx | 8 +- .../[listingId]/ListingDetailClient.tsx | 105 ++++++++++++------ src/app/shops/new/location/page.tsx | 103 +++++++++-------- src/app/shops/new/logo/page.tsx | 13 ++- src/app/shops/payment/failed/page.tsx | 4 +- src/app/shops/product/[catalogId]/page.tsx | 2 +- .../profile/[shopId]/ShopProfileClient.tsx | 72 ++++++------ src/components/TabNavigation.tsx | 17 ++- src/components/chat/GiftInChatModal.tsx | 6 +- src/components/course/CourseManager.tsx | 9 +- src/components/models/ModelsFilter.tsx | 6 +- .../Workroom/ProjectRequestsAction.tsx | 8 +- .../Workroom/ProjectWorkroomActions.tsx | 4 +- .../projects/Workroom/ProjectWorkroomRate.tsx | 7 +- .../MyBillboardStatusActions.tsx | 8 +- .../shops/ProductCategoryPicker.tsx | 12 +- src/components/shops/ShopCartTab.tsx | 2 +- src/components/shops/ShopContactInfoModal.tsx | 26 +++-- src/components/shops/ShopFavoritesTab.tsx | 2 +- src/components/shops/ShopProductAuthor.tsx | 6 +- src/components/shops/ShopProductGrid.tsx | 5 +- src/components/shops/ShopProductListRow.tsx | 18 ++- src/lib/ui/buttonStyles.ts | 5 +- src/types/types.ts | 7 +- 40 files changed, 505 insertions(+), 255 deletions(-) create mode 100644 public/images/icons/brand/instagram-brand.svg create mode 100644 public/images/icons/brand/phone-brand.svg create mode 100644 public/images/icons/brand/telegram-brand.svg create mode 100644 public/images/icons/brand/whatsapp-brand.svg diff --git a/public/images/icons/brand/instagram-brand.svg b/public/images/icons/brand/instagram-brand.svg new file mode 100644 index 0000000..30af5b8 --- /dev/null +++ b/public/images/icons/brand/instagram-brand.svg @@ -0,0 +1 @@ + diff --git a/public/images/icons/brand/phone-brand.svg b/public/images/icons/brand/phone-brand.svg new file mode 100644 index 0000000..4f90f3f --- /dev/null +++ b/public/images/icons/brand/phone-brand.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/public/images/icons/brand/telegram-brand.svg b/public/images/icons/brand/telegram-brand.svg new file mode 100644 index 0000000..23ebfb4 --- /dev/null +++ b/public/images/icons/brand/telegram-brand.svg @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/public/images/icons/brand/whatsapp-brand.svg b/public/images/icons/brand/whatsapp-brand.svg new file mode 100644 index 0000000..b83c26f --- /dev/null +++ b/public/images/icons/brand/whatsapp-brand.svg @@ -0,0 +1,26 @@ + + + + + + + + + diff --git a/src/app/(projects)/academy/[id]/[title]/CourseClient.tsx b/src/app/(projects)/academy/[id]/[title]/CourseClient.tsx index 9f91497..0e40ceb 100644 --- a/src/app/(projects)/academy/[id]/[title]/CourseClient.tsx +++ b/src/app/(projects)/academy/[id]/[title]/CourseClient.tsx @@ -524,9 +524,11 @@ export default function CourseDetail({ } if (paymentUrl) { - window.open(paymentUrl, "_blank", "noopener,noreferrer"); - toast.success(t("academy.course.redirectedToPayment")); - + // window.open بعدِ یه await در سافاری (دسکتاپ و خصوصاً iOS) پاپ‌آپ‌بلاک می‌شه — + // چون دیگه synchronously به همون کلیکِ کاربر متصل نیست. window.location.href هم + // مطمئن‌تره، هم با بقیه‌ی مسیرهای پرداختِ این اپ (booking، پروژه، بیلبورد) یکی می‌مونه + window.location.href = paymentUrl; + return; } else { console.error("ساختار پاسخ نامعتبر:", response); toast.error( diff --git a/src/app/(projects)/academy/payment/failed/page.tsx b/src/app/(projects)/academy/payment/failed/page.tsx index 9999722..c6aba2d 100644 --- a/src/app/(projects)/academy/payment/failed/page.tsx +++ b/src/app/(projects)/academy/payment/failed/page.tsx @@ -82,7 +82,9 @@ function FailedProject() { } else { const authority = response.authority; const paymentUrl = `https://www.zarinpal.com/pg/StartPay/${authority}`; - router.push(paymentUrl); + // لینک خارجی — router.push رفتارِ تضمین‌شده‌ای نداره؛ window.location.href + // مطمئن‌تره (خصوصاً سافاری) + window.location.href = paymentUrl; } } catch (error: unknown) { console.log(error); diff --git a/src/app/billboards/new/[id]/page.tsx b/src/app/billboards/new/[id]/page.tsx index 518c00c..2644434 100644 --- a/src/app/billboards/new/[id]/page.tsx +++ b/src/app/billboards/new/[id]/page.tsx @@ -66,7 +66,9 @@ function BillboardPayment({ params }: IBillboardProps) { } else { const authority = response.authority; const paymentUrl = `https://www.zarinpal.com/pg/StartPay/${authority}`; - router.push(paymentUrl); + // لینک خارجی — router.push رفتارِ تضمین‌شده‌ای نداره؛ window.location.href + // مطمئن‌تره (خصوصاً سافاری) + window.location.href = paymentUrl; } } catch (error: unknown) { console.log(error); diff --git a/src/app/billboards/payment/failed/page.tsx b/src/app/billboards/payment/failed/page.tsx index 03eb0c3..e448d2d 100644 --- a/src/app/billboards/payment/failed/page.tsx +++ b/src/app/billboards/payment/failed/page.tsx @@ -56,7 +56,9 @@ function FailedBillboard() { } else { const authority = response.authority; const paymentUrl = `https://www.zarinpal.com/pg/StartPay/${authority}`; - router.push(paymentUrl); + // لینک خارجی — router.push رفتارِ تضمین‌شده‌ای نداره؛ window.location.href + // مطمئن‌تره (خصوصاً سافاری) + window.location.href = paymentUrl; } } catch (error: unknown) { console.log(error); @@ -80,7 +82,9 @@ function FailedBillboard() { } else { const authority = response.authority; const paymentUrl = `https://www.zarinpal.com/pg/StartPay/${authority}`; - router.push(paymentUrl); + // لینک خارجی — router.push رفتارِ تضمین‌شده‌ای نداره؛ window.location.href + // مطمئن‌تره (خصوصاً سافاری) + window.location.href = paymentUrl; } } catch (error: unknown) { console.log(error); diff --git a/src/app/gift/[userId]/page.tsx b/src/app/gift/[userId]/page.tsx index 63a8b4b..3503dc2 100644 --- a/src/app/gift/[userId]/page.tsx +++ b/src/app/gift/[userId]/page.tsx @@ -9,7 +9,6 @@ import useAxios from "@/hooks/useAxios"; import { filterChipClass } from "@/lib/ui/buttonStyles"; import { User } from "@/types/types"; import React, { useEffect, useMemo, useState } from "react"; -import { useRouter } from "next/navigation"; import toast from "react-hot-toast"; import { useTranslation } from "react-i18next"; @@ -25,7 +24,6 @@ function GiftPage({ params }: GiftPageProps) { const { t } = useTranslation("common"); const { userId } = React.use(params); const { request } = useAxios(); - const router = useRouter(); const [userDetail, setUserDetail] = useState(); const [selectedAmount, setSelectedAmount] = useState( @@ -67,10 +65,12 @@ function GiftPage({ params }: GiftPageProps) { "/gifts/initiate", { receiverId: userId, amount: finalAmount, message } ); + // لینک خارجی — router.push رفتارِ تضمین‌شده‌ای نداره؛ window.location.href + // مطمئن‌تره (خصوصاً سافاری) if (response?.paymentUrl) { - router.push(response.paymentUrl); + window.location.href = response.paymentUrl; } else if (response?.authority) { - router.push(`https://www.zarinpal.com/pg/StartPay/${response.authority}`); + window.location.href = `https://www.zarinpal.com/pg/StartPay/${response.authority}`; } else { toast.error(t("giftPage.paymentInfoError")); } diff --git a/src/app/offer/[id]/page.tsx b/src/app/offer/[id]/page.tsx index 5fc1f2d..997fe7e 100644 --- a/src/app/offer/[id]/page.tsx +++ b/src/app/offer/[id]/page.tsx @@ -158,13 +158,14 @@ function TicketChat({ params }: ITicketChatProps) { setHasUsedFreeOffer(true); router.push(`/offer/payment/success?userId=${id}`); } else if (data.paymentUrl) { - window.open(data.paymentUrl, "_blank", "noopener,noreferrer"); + // window.open بعدِ await در سافاری (دسکتاپ/iOS) پاپ‌آپ‌بلاک می‌شه چون دیگه + // synchronously به کلیکِ کاربر متصل نیست؛ ضمناً بازگشتِ ZarinPal (از طریق + // offer/payment-web/route.js) کلِ همون تب رو به "/" ریدایرکت می‌کنه، نه تبِ + // اصلی — پس با _blank دو تب باز می‌موند. window.location.href هم امن‌تره + // هم با بقیه‌ی مسیرهای پرداختِ این اپ یکی می‌مونه + window.location.href = data.paymentUrl; } else if (data.authority) { - window.open( - `https://www.zarinpal.com/pg/StartPay/${data.authority}`, - "_blank", - "noopener,noreferrer" - ); + window.location.href = `https://www.zarinpal.com/pg/StartPay/${data.authority}`; } else { toast.error(t("offerPage.paymentInfoError")); } diff --git a/src/app/settings/shop/list/page.tsx b/src/app/settings/shop/list/page.tsx index 331830f..268ee7c 100644 --- a/src/app/settings/shop/list/page.tsx +++ b/src/app/settings/shop/list/page.tsx @@ -8,8 +8,9 @@ import Header from "@/components/main/Header"; import ShopListCard from "@/components/shops/ShopListCard"; import BoldIcon from "@/components/ui/BoldIcon"; import useAxios from "@/hooks/useAxios"; +import { useQuery } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; -import { useEffect, useMemo, useState } from "react"; +import { useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; type MyShop = { @@ -28,15 +29,19 @@ export default function ShopSettingsPage() { const { t } = useTranslation("common"); const router = useRouter(); const { request } = useAxios(); - const [myShops, setMyShops] = useState(null); const [search, setSearch] = useState(""); - useEffect(() => { - request<{ shops: MyShop[] }>("GET", "/shops/mine") - .then((res) => setMyShops(res?.shops || [])) - .catch(() => setMyShops([])); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + // طبق خواسته‌ی کاربر: بعدِ برگشت از ویرایشِ فروشگاه بدونِ رفرشِ دستی تغییرات دیده + // بشن — قبلاً با useEffect(..., []) فقط یه‌بار موقعِ اولین mount فچ می‌شد؛ عینِ + // useInfiniteScroll (لیستِ کالاها) با refetchOnMount:"always" هر بار برگردیم به + // این صفحه دوباره فچ می‌شه + const { data } = useQuery({ + queryKey: ["shops-mine"], + queryFn: () => request<{ shops: MyShop[] }>("GET", "/shops/mine"), + staleTime: 0, + refetchOnMount: "always", + }); + const myShops = data?.shops ?? null; const filteredShops = useMemo(() => { if (!myShops) return []; diff --git a/src/app/settings/wallet/page.tsx b/src/app/settings/wallet/page.tsx index 44c82eb..15e450f 100644 --- a/src/app/settings/wallet/page.tsx +++ b/src/app/settings/wallet/page.tsx @@ -8,7 +8,7 @@ import LocalePageShell from "@/components/i18n/LocalePageShell"; import useAxios from "@/hooks/useAxios"; import { toggleBtnClass } from "@/lib/ui/buttonStyles"; import { cn } from "@/lib/utils"; -import { useRouter, useSearchParams } from "next/navigation"; +import { useSearchParams } from "next/navigation"; import { useEffect, useState } from "react"; import toast from "react-hot-toast"; import { useTranslation } from "react-i18next"; @@ -31,7 +31,6 @@ type WalletSummary = { export default function WalletSettingsPage() { const { t } = useTranslation("common"); - const router = useRouter(); const searchParams = useSearchParams(); const { request, loading } = useAxios(); @@ -88,7 +87,9 @@ export default function WalletSettingsPage() { { amount } ); if (response?.authority) { - router.push(`https://www.zarinpal.com/pg/StartPay/${response.authority}`); + // لینک خارجی — router.push رفتارِ تضمین‌شده‌ای نداره؛ window.location.href + // مطمئن‌تره (خصوصاً سافاری) + window.location.href = `https://www.zarinpal.com/pg/StartPay/${response.authority}`; } } catch (err: unknown) { const message = diff --git a/src/app/shops/[shopId]/page.tsx b/src/app/shops/[shopId]/page.tsx index 09a6873..8fd5e82 100644 --- a/src/app/shops/[shopId]/page.tsx +++ b/src/app/shops/[shopId]/page.tsx @@ -8,6 +8,7 @@ import { IMAGE_BASE_URL, staticIconUrl } from "@/components/main/BaseUrl"; import ShareShopModal from "@/components/shops/ShareShopModal"; import ShopProductListRow from "@/components/shops/ShopProductListRow"; import PageTitle from "@/components/settings/PageTitle"; +import VerificationBadge from "@/components/main/VerificationBadge"; import BoldIcon from "@/components/ui/BoldIcon"; import TabNavigation from "@/components/TabNavigation"; import useAxios from "@/hooks/useAxios"; @@ -25,10 +26,12 @@ type ShopHeader = { _id: string; name: string; logo?: string | null; + owner?: { verify_badge?: string | null } | null; }; export default function ShopProductsPage() { - const { t } = useTranslation("common"); + const { t, i18n } = useTranslation("common"); + const isFa = (i18n.language || "fa").toLowerCase().startsWith("fa"); const router = useRouter(); const params = useParams<{ shopId: string }>(); const shopId = params.shopId; @@ -73,43 +76,55 @@ export default function ShopProductsPage() {
-
-
- - {shopId && ( - - {shop?.logo ? ( - // eslint-disable-next-line @next/next/no-img-element - {shop.name} - ) : ( - - )} - - )} -
+ {shop?.logo ? ( + // eslint-disable-next-line @next/next/no-img-element + {shop.name} + ) : ( + + )} + + )} - {shop?.name} + + {shop?.name} + + + +
{t("shops.myProductsTitle")} diff --git a/src/app/shops/[shopId]/products/[listingId]/page.tsx b/src/app/shops/[shopId]/products/[listingId]/page.tsx index 89eacc2..92c5781 100644 --- a/src/app/shops/[shopId]/products/[listingId]/page.tsx +++ b/src/app/shops/[shopId]/products/[listingId]/page.tsx @@ -11,6 +11,7 @@ import TabNavigation from "@/components/TabNavigation"; import useAxios from "@/hooks/useAxios"; import { saveProductWizardId } from "@/hooks/useProductWizardId"; import { cn } from "@/lib/utils"; +import { copyTextToClipboard } from "@/lib/chat/getCopyableMessageText"; import { IShopProductListing, IShopProductVariant } from "@/types/types"; import { useQueryClient } from "@tanstack/react-query"; import Image from "next/image"; @@ -197,6 +198,23 @@ export default function ProductManagePage() { router.push(`/shops/${shopId}/products/new/name?edit=1`); }; + // طبق خواسته‌ی کاربر: اشتراک‌گذاری لینکِ خودِ کالا — عینِ ListingDetailClient.tsx + const shareProduct = async () => { + if (!listing?._id) return; + const productUrl = `https://modstagram.com/shops/listing/${listing._id}`; + if (typeof navigator !== "undefined" && navigator.share) { + try { + await navigator.share({ url: productUrl, title: listing.title }); + return; + } catch { + return; + } + } + const ok = await copyTextToClipboard(productUrl); + if (ok) toast.success(t("shops.share.copied")); + else toast.error(t("shops.share.copyFailed")); + }; + if (!listing) return null; const images = listing.images || []; @@ -213,33 +231,52 @@ export default function ProductManagePage() {
-
+
- {shopInfo?.name} - - {shopInfo?.logo ? ( - // eslint-disable-next-line @next/next/no-img-element - {shopInfo.name} - ) : ( - - )} -
-
+ {/* طبق خواسته‌ی کاربر: نام+لوگوی فروشگاه راست (فرزند اول)، عینِ اندروید، و + اشتراک‌گذاری روبروی نام سمت چپ (فرزند آخر) */} +
+
+ + {shopInfo?.logo ? ( + // eslint-disable-next-line @next/next/no-img-element + {shopInfo.name} + ) : ( + + )} + + {shopInfo?.name} +
+ +
+ + {/* گردی گوشه‌های کادر ۱۶px (rounded-2xl) عینِ اندروید (ProductDetailScreen.tsx: + card.borderRadius=16) — قبلاً rounded-3xl (۲۴px) بود */} +
+ {/* عینِ اندروید: عنوان راست (فرزند اول)، بجِ تخفیف چپ (فرزند آخر) — قبلاً + برعکس بود (بج قبل از عنوان) */}
- {discountPercent != null && }

{listing.title}

+ {discountPercent != null && }
{images.length > 0 && ( diff --git a/src/app/shops/[shopId]/products/new/name/page.tsx b/src/app/shops/[shopId]/products/new/name/page.tsx index 4011df0..1084a1c 100644 --- a/src/app/shops/[shopId]/products/new/name/page.tsx +++ b/src/app/shops/[shopId]/products/new/name/page.tsx @@ -138,7 +138,11 @@ function ProductNamePage() {
- + {/* پیش‌فرضِ router.back() اینجا کاربر رو برمی‌گردوند به هرجایی که ازش وارد این + ویزارد شده — که برای ویرایش، صفحه‌ی نمایشِ کالا + (shops/[shopId]/products/[listingId]) بود، نه لیستِ کالاها. طبقِ خواسته‌ی + کاربر همیشه صریحاً می‌ره به لیستِ کالاهای همین فروشگاه */} +
{t("shops.productNameTitle")} diff --git a/src/app/shops/[shopId]/products/new/variants/page.tsx b/src/app/shops/[shopId]/products/new/variants/page.tsx index 0b6d34d..027a115 100644 --- a/src/app/shops/[shopId]/products/new/variants/page.tsx +++ b/src/app/shops/[shopId]/products/new/variants/page.tsx @@ -297,6 +297,7 @@ function ProductVariantsPage() { loading={loading} disabled={loading || rows.length === 0} onClick={handleSubmit} + className="border-[#ef4444] bg-[#ef4444] text-white hover:bg-[#ef4444]/90 dark:bg-[#ef4444] dark:hover:bg-[#ef4444]/90" > {t("shops.saveProduct")} diff --git a/src/app/shops/checkout/[orderId]/page.tsx b/src/app/shops/checkout/[orderId]/page.tsx index 4d4fbfe..acf3815 100644 --- a/src/app/shops/checkout/[orderId]/page.tsx +++ b/src/app/shops/checkout/[orderId]/page.tsx @@ -6,7 +6,7 @@ import PageTitle from "@/components/settings/PageTitle"; import AuthNextButton from "@/components/auth/AuthNextButton"; import { IMAGE_BASE_URL } from "@/components/main/BaseUrl"; import useAxios from "@/hooks/useAxios"; -import { useParams, useRouter } from "next/navigation"; +import { useParams } from "next/navigation"; import { useEffect, useState } from "react"; import Image from "next/image"; import toast from "react-hot-toast"; @@ -34,7 +34,6 @@ type OrderDetail = { export default function CheckoutPage() { const { t } = useTranslation("common"); - const router = useRouter(); const params = useParams<{ orderId: string }>(); const { request, loading } = useAxios(); const [order, setOrder] = useState(null); @@ -54,7 +53,10 @@ export default function CheckoutPage() { { orderId: params.orderId } ); if (response?.authority) { - router.push(`https://www.zarinpal.com/pg/StartPay/${response.authority}`); + // router.push (next/navigation) برای URLهای بیرونی رفتارِ تضمین‌شده‌ای نداره — + // window.location.href مطمئن‌ترین راهه (خصوصاً سافاری) و با بقیه‌ی مسیرهای + // پرداختِ این اپ (booking، آکادمی) هم یکی می‌مونه + window.location.href = `https://www.zarinpal.com/pg/StartPay/${response.authority}`; } } catch (err: unknown) { const message = diff --git a/src/app/shops/listing/[listingId]/ListingDetailClient.tsx b/src/app/shops/listing/[listingId]/ListingDetailClient.tsx index 4e19784..40239db 100644 --- a/src/app/shops/listing/[listingId]/ListingDetailClient.tsx +++ b/src/app/shops/listing/[listingId]/ListingDetailClient.tsx @@ -10,7 +10,9 @@ import BackButton from "@/components/ui/BackButton"; import DiscountBadge from "@/components/ui/DiscountBadge"; import TabNavigation from "@/components/TabNavigation"; import useAxios from "@/hooks/useAxios"; +import { copyTextToClipboard } from "@/lib/chat/getCopyableMessageText"; import { cn } from "@/lib/utils"; +import { btnDefault, btnPrimary } from "@/lib/ui/buttonStyles"; import { IShopProductListing, IShopProductVariant } from "@/types/types"; import Cookies from "js-cookie"; import { trackExploreInteraction } from "@/api/trackExploreInteraction"; @@ -94,6 +96,24 @@ export default function ListingDetailClient() { const shopInfo: ShopHeader | null = listing && typeof listing.shop === "object" ? (listing.shop as ShopHeader) : null; + // طبق خواسته‌ی کاربر: اشتراک‌گذاری لینکِ خودِ کالا، نه فروشگاه — عینِ shareProduct + // توی ProductDetailScreen.tsx اندروید (Web Share API با fallback به کپیِ لینک) + const shareProduct = async () => { + if (!listing?._id) return; + const productUrl = `https://modstagram.com/shops/listing/${listing._id}`; + if (typeof navigator !== "undefined" && navigator.share) { + try { + await navigator.share({ url: productUrl, title: listing.title }); + return; + } catch { + return; + } + } + const ok = await copyTextToClipboard(productUrl); + if (ok) toast.success(t("shops.share.copied")); + else toast.error(t("shops.share.copyFailed")); + }; + useEffect(() => { if (!listing?._id) return; const token = Cookies.get("token") || ""; @@ -164,6 +184,10 @@ export default function ListingDetailClient() { }, [matchedVariant?._id]); const enabledShippingMethods = shopInfo?.shipping_methods?.filter((m) => m.enabled) || []; + // هزینه‌ی روشِ ارسالِ انتخاب‌شده — قبلاً هیچ‌جا خونده نمی‌شد، پس انتخابِ روشِ ارسال + // هیچ تاثیری روی قیمتِ نمایش‌داده‌شده نداشت (فقط موقعِ ثبتِ سفارش/handleBuy چک می‌شد) + const shippingCost = + enabledShippingMethods.find((m) => m.method === shippingMethod)?.cost || 0; const categoryLine = listing ? [listing.category, listing.sub_category, listing.sub_sub_category] @@ -256,34 +280,47 @@ export default function ListingDetailClient() {
+ {/* طبق خواسته‌ی کاربر: دکمه‌ی اشتراک‌گذاریِ خودِ کالا (نه فروشگاه) روبروی نام + فروشگاه سمت چپ — عینِ ProductDetailScreen.tsx اندروید */} {shopInfo?._id ? ( - - - {shopInfo?.logo ? ( - // eslint-disable-next-line @next/next/no-img-element - {shopInfo.name} - ) : ( - - )} - - {shopInfo?.name} - +
+ + + {shopInfo?.logo ? ( + // eslint-disable-next-line @next/next/no-img-element + {shopInfo.name} + ) : ( + + )} + + {shopInfo?.name} + + +
) : null} -
+ {/* گردی گوشه‌های کادر ۱۶px (rounded-2xl) عینِ اندروید — قبلاً rounded-3xl (۲۴px) بود */} +

{listing.title}

{discountPercent != null && ( @@ -432,14 +469,14 @@ export default function ListingDetailClient() { )} - {/* عین ProductDetailScreen.tsx اندروید: دکمه‌ی ثبت خرید با colors.success (#387E65)، + {/* عین ProductDetailScreen.tsx اندروید: دکمه‌ی ثبت خرید با colors.danger (#ef4444)، و اسپینر تعداد یک پیل بوردردار خنثی با دکمه‌های +/- متنی، نه دو دکمه‌ی دایره‌ای رنگی */}
@@ -471,15 +508,15 @@ export default function ListingDetailClient() { {matchedVariant.discount_price ? ( - {(matchedVariant.price * quantity).toLocaleString()} {t("settings.toman")} + {(matchedVariant.price * quantity + shippingCost).toLocaleString()} {t("settings.toman")} - {(matchedVariant.discount_price * quantity).toLocaleString()} {t("settings.toman")} + {(matchedVariant.discount_price * quantity + shippingCost).toLocaleString()} {t("settings.toman")} ) : ( - {(matchedVariant.price * quantity).toLocaleString()} {t("settings.toman")} + {(matchedVariant.price * quantity + shippingCost).toLocaleString()} {t("settings.toman")} )} @@ -487,19 +524,19 @@ export default function ListingDetailClient() {
- {/* عین ProductDetailScreen.tsx اندروید: سبد خرید با colors.info (#17337B) توپر، - علاقه‌مندی فقط حاشیه‌دارِ همون رنگ (نه پرشده) */} + {/* طبق خواسته‌ی کاربر: همون رنگ/استایلِ دکمه‌های اعمال/پاک‌کردنِ فیلترها — + سبد خرید مثل «اعمال» (btnPrimary)، علاقه‌مندی مثل «پاک کردن» (btnDefault) */} diff --git a/src/app/shops/new/location/page.tsx b/src/app/shops/new/location/page.tsx index ee09275..56b9bfb 100644 --- a/src/app/shops/new/location/page.tsx +++ b/src/app/shops/new/location/page.tsx @@ -129,8 +129,62 @@ function ShopLocationPage() { {t("shops.locationTitle")} + {/* طبق خواسته‌ی کاربر: استان/شهر همیشه باز و اجباری‌ان (چه حضوری داشته باشه چه + نه)، فقط نقشه/محله/آدرسِ دقیق مخصوصِ حالتِ حضوریه و مخفی می‌شن؛ تیکِ «فروشگاه + حضوری ندارم» هم رفت زیرِ آدرس — عینِ اندروید (NewShopLocationScreen.tsx) */}
-