feat(seller-dashboard): desktop-friendly layouts for list and hub pages
Redesign mobile-style seller dashboard pages for desktop (lg:) while leaving the mobile view unchanged: - products: table-style header row (product/price/variant/actions) with a bordered card wrapper and an "add product" button; accordion rows align to the columns, inline price hidden on desktop. - orders: pill-style status tabs + search on one row, orders shown as a two-column card grid instead of stretched full-width rows. - financial-dashboard: three-column card grid with icon chips. - setting: status/theme as side-by-side cards, menu items as a three-column card grid (logout styled in red). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
4af143bccf
commit
257871d5f1
@ -70,7 +70,13 @@ export default function FinancialDashboard() {
|
||||
];
|
||||
return (
|
||||
<div className="bg-WHITE">
|
||||
<div className="lg:hidden">
|
||||
<HeaderWithSupport title="داشبورد مالی" onBack={handleBack} />
|
||||
</div>
|
||||
<h2 className="hidden lg:block text-[24px] font-extrabold mb-6">
|
||||
داشبورد مالی
|
||||
</h2>
|
||||
<div className="lg:grid lg:grid-cols-3 lg:gap-4">
|
||||
{menuItems.map((item, index) => (
|
||||
<div
|
||||
onClick={() => {
|
||||
@ -84,18 +90,21 @@ export default function FinancialDashboard() {
|
||||
cashFunds(item.link);
|
||||
}
|
||||
}}
|
||||
className="border-b flex items-center w-full p-4 bg-WHITE hover:bg-WHITE3 cursor-pointer"
|
||||
className="border-b flex items-center w-full p-4 bg-WHITE hover:bg-WHITE3 cursor-pointer lg:border lg:border-WHITE3 lg:rounded-2xl lg:p-6 lg:items-start lg:hover:shadow-md lg:transition-shadow"
|
||||
>
|
||||
<div className="flex flex-col w-full gap-2">
|
||||
<div className="flex flex-row w-full gap-2">
|
||||
<div className="flex flex-row w-full gap-2 lg:items-center">
|
||||
<span className="lg:w-11 lg:h-11 lg:rounded-xl lg:bg-WHITE2 lg:flex lg:items-center lg:justify-center">
|
||||
{item.icon}
|
||||
<p className="text-R14 font-bold">{item.title}</p>
|
||||
</span>
|
||||
<p className="text-R14 lg:text-B16 font-bold">{item.title}</p>
|
||||
</div>
|
||||
<p className="text-R12">{item.description}</p>
|
||||
<p className="text-R12 text-GRAY lg:mt-1">{item.description}</p>
|
||||
</div>
|
||||
<ChevronLeft size={16} />
|
||||
<ChevronLeft size={16} className="lg:hidden" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -129,7 +129,7 @@ function OrderItem({
|
||||
navigate(`/store/${storeId}/orders/${order.id}`);
|
||||
}
|
||||
}}
|
||||
className={`border-t ${isLastOrder && "border-b"} p-2 bg-WHITE hover:bg-WHITE3 cursor-pointer transition-colors duration-300`}
|
||||
className={`border-t ${isLastOrder && "border-b"} p-2 bg-WHITE hover:bg-WHITE3 cursor-pointer transition-colors duration-300 lg:border lg:border-WHITE3 lg:rounded-2xl lg:p-5 lg:hover:shadow-md lg:hover:bg-WHITE`}
|
||||
>
|
||||
<div className="flex flex-col space-y-3">
|
||||
{/* Header */}
|
||||
@ -235,22 +235,29 @@ export default function Orders() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-WHITE">
|
||||
<div className="bg-WHITE lg:bg-transparent">
|
||||
{/* Header */}
|
||||
<div className="lg:hidden">
|
||||
<HeaderWithSupport title="سفارشهای دریافتی" onBack={handleBack} />
|
||||
</div>
|
||||
<h1 className="hidden lg:block text-[24px] font-extrabold mt-2">
|
||||
سفارشهای دریافتی
|
||||
</h1>
|
||||
|
||||
<div className="py-4">
|
||||
{/* Tabs + Search row (desktop puts them side by side) */}
|
||||
<div className="lg:flex lg:items-center lg:justify-between lg:gap-4 lg:mb-4">
|
||||
{/* Tabs */}
|
||||
<div className="bg-WHITE px-4 mb-4 overflow-hidden">
|
||||
<div className="overflow-x-auto grid grid-cols-4">
|
||||
<div className="bg-WHITE lg:bg-transparent px-4 lg:px-0 mb-4 lg:mb-0 overflow-hidden">
|
||||
<div className="overflow-x-auto grid grid-cols-4 lg:flex lg:gap-2 lg:w-auto">
|
||||
{orderTabs.map((tab) => (
|
||||
<button
|
||||
key={tab.key}
|
||||
onClick={() => setActiveTab(tab.key)}
|
||||
className={`flex-shrink-0 flex flex-col items-center justify-center gap-1 py-1 text-R12 font-medium border-b-2 transition-colors ${
|
||||
className={`flex-shrink-0 flex flex-col lg:flex-row items-center justify-center gap-1 lg:gap-2 py-1 lg:py-2 lg:px-4 text-R12 lg:text-[13px] font-medium border-b-2 lg:border-b-0 lg:rounded-xl transition-colors lg:[&_svg]:size-4 ${
|
||||
activeTab === tab.key
|
||||
? ""
|
||||
: "border-transparent text-GRAY hover:text-BLACK2 hover:bg-WHITE3"
|
||||
? "lg:bg-BLACK lg:text-white"
|
||||
: "border-transparent text-GRAY hover:text-BLACK2 hover:bg-WHITE3 lg:border lg:border-WHITE3"
|
||||
}`}
|
||||
>
|
||||
{tab.icon}
|
||||
@ -261,7 +268,7 @@ export default function Orders() {
|
||||
</div>
|
||||
|
||||
{/* Search Bar */}
|
||||
<div className="mb-4 px-4">
|
||||
<div className="mb-4 lg:mb-0 px-4 lg:px-0 lg:w-72 lg:shrink-0">
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
@ -275,10 +282,11 @@ export default function Orders() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Orders List */}
|
||||
{filteredOrders.length > 0 ? (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-4 lg:space-y-0 lg:grid lg:grid-cols-2 lg:gap-4">
|
||||
{filteredOrders.map((order, index) => (
|
||||
<OrderItem
|
||||
key={order.id}
|
||||
|
||||
@ -51,7 +51,7 @@ function ProductAccordionItem({
|
||||
onClick={onToggle}
|
||||
className="w-full px-4 py-3 flex items-center justify-between hover:bg-WHITE3 transition-colors"
|
||||
>
|
||||
<div className="flex items-center gap-4 flex-1">
|
||||
<div className="flex items-center gap-4 flex-1 min-w-0">
|
||||
{product.primaryImage && (
|
||||
<img
|
||||
src={
|
||||
@ -63,23 +63,42 @@ function ProductAccordionItem({
|
||||
e.currentTarget.src =
|
||||
theme === "dark" ? placeholderImageDark : placeholderImage;
|
||||
}}
|
||||
className="w-12 h-12 object-contain rounded"
|
||||
className="w-12 h-12 lg:w-14 lg:h-14 object-contain rounded"
|
||||
/>
|
||||
)}
|
||||
<div className="text-right">
|
||||
<h3 className="font-medium text-sm">{product.title}</h3>
|
||||
<div className="text-right min-w-0">
|
||||
<h3 className="font-medium text-sm lg:text-[15px] truncate">
|
||||
{product.title}
|
||||
</h3>
|
||||
{/* Price shown inline on mobile only; desktop uses its own column */}
|
||||
{product.basePrice && (
|
||||
<p className="text-xs text-gray-600 mt-1">
|
||||
<p className="text-xs text-gray-600 mt-1 lg:hidden">
|
||||
{parseInt(product.basePrice).toLocaleString("fa-IR")} تومان
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{variants && variants.length > 0 && (
|
||||
{/* Desktop price column */}
|
||||
<span className="hidden lg:block w-32 text-center text-[14px] font-semibold">
|
||||
{product.basePrice
|
||||
? `${parseInt(product.basePrice).toLocaleString("fa-IR")} تومان`
|
||||
: "—"}
|
||||
</span>
|
||||
{/* Desktop variants column */}
|
||||
<span className="hidden lg:flex w-24 justify-center">
|
||||
{variants && variants.length > 0 ? (
|
||||
<span className="text-xs text-gray-500 bg-gray-100 px-2 py-1 rounded">
|
||||
{variants.length} نوع
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-GRAY">—</span>
|
||||
)}
|
||||
</span>
|
||||
<div className="flex items-center gap-2 lg:w-28 lg:justify-center">
|
||||
{variants && variants.length > 0 && (
|
||||
<span className="text-xs text-gray-500 bg-gray-100 px-2 py-1 rounded lg:hidden">
|
||||
{variants.length} نوع
|
||||
</span>
|
||||
)}
|
||||
<button
|
||||
onClick={handleEditProduct}
|
||||
@ -171,6 +190,7 @@ function ProductAccordionItem({
|
||||
|
||||
function StoreProductsContent() {
|
||||
const { storeId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const [openProductId, setOpenProductId] = useState<string | null>(null);
|
||||
const [stickyProductId, setStickyProductId] = useState<string | null>(null);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
@ -227,13 +247,31 @@ function StoreProductsContent() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="bg-WHITE">
|
||||
<div className="border-b border-gray-200 px-4 py-3">
|
||||
<h1 className="text-lg font-semibold text-right">محصولات فروشگاه</h1>
|
||||
<div ref={containerRef} className="bg-WHITE lg:bg-transparent">
|
||||
<div className="border-b border-gray-200 px-4 py-3 lg:border-0 lg:px-0 lg:py-0 lg:mb-6 lg:flex lg:items-end lg:justify-between">
|
||||
<div>
|
||||
<h1 className="text-lg font-semibold text-right lg:text-[24px] lg:font-extrabold">
|
||||
محصولات فروشگاه
|
||||
</h1>
|
||||
<p className="text-sm text-gray-500 mt-1">{products.length} محصول</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => navigate(`/store/add/manual/${storeId}`)}
|
||||
className="hidden lg:inline-flex items-center gap-2 rounded-xl bg-VITROWN_BLUE text-white px-5 py-2.5 text-[14px] font-bold hover:opacity-90 transition"
|
||||
>
|
||||
افزودن محصول جدید
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="divide-y divide-gray-200">
|
||||
{/* Desktop table-style header */}
|
||||
<div className="hidden lg:flex items-center px-4 py-3 text-GRAY text-[13px] font-bold border border-WHITE3 rounded-t-2xl bg-WHITE2">
|
||||
<span className="flex-1">محصول</span>
|
||||
<span className="w-32 text-center">قیمت</span>
|
||||
<span className="w-24 text-center">تنوع</span>
|
||||
<span className="w-28 text-center">عملیات</span>
|
||||
</div>
|
||||
|
||||
<div className="divide-y divide-gray-200 lg:border lg:border-t-0 lg:border-WHITE3 lg:rounded-b-2xl lg:overflow-hidden lg:bg-WHITE">
|
||||
{products.map((product) =>
|
||||
product.id ? (
|
||||
<ProductAccordionItem
|
||||
|
||||
@ -141,17 +141,26 @@ export default function StoreSetting() {
|
||||
return (
|
||||
<div className="flex flex-col bg-WHITE">
|
||||
{/* Header */}
|
||||
<div className="lg:hidden">
|
||||
<HeaderWithSupport title="تنظیمات فروشگاه" onBack={handleBack} />
|
||||
</div>
|
||||
<h2 className="hidden lg:block text-[24px] font-extrabold mb-6">
|
||||
تنظیمات فروشگاه
|
||||
</h2>
|
||||
|
||||
{/* Status + Theme cards */}
|
||||
<div className="lg:grid lg:grid-cols-2 lg:gap-4 lg:mb-6">
|
||||
{/* Store Status */}
|
||||
<div className="p-4 bg-WHITE border-b border-inner-border">
|
||||
<div className="p-4 bg-WHITE border-b border-inner-border lg:border lg:border-WHITE3 lg:rounded-2xl lg:p-6">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="flex flex-col gap-1 w-full">
|
||||
<div className="flex w-full gap-1 items-center">
|
||||
<Code size={20} />
|
||||
<p className="text-R12">وضعیت فروشگاه (فعال)</p>
|
||||
<p className="text-R12 lg:text-B16 font-bold">
|
||||
وضعیت فروشگاه (فعال)
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-R10 text-GRAY">
|
||||
<p className="text-R10 lg:text-R12 text-GRAY">
|
||||
در حالت فعال، فروشگاه برای مخاطبان نمایش داده میشود.
|
||||
</p>
|
||||
</div>
|
||||
@ -168,28 +177,33 @@ export default function StoreSetting() {
|
||||
</div>
|
||||
|
||||
{/* Theme Toggle */}
|
||||
<div className="p-4 bg-WHITE border-b border-inner-border">
|
||||
<div className="p-4 bg-WHITE border-b border-inner-border lg:border lg:border-WHITE3 lg:rounded-2xl lg:p-6">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-R12">
|
||||
<p className="text-R12 lg:text-B16 font-bold">
|
||||
{initialTheme === "dark" ? "حالت تاریک" : "حالت روشن"}
|
||||
</p>
|
||||
<p className="text-R10 text-GRAY">
|
||||
<p className="text-R10 lg:text-R12 text-GRAY">
|
||||
تغییر حالت نمایش به تاریک یا روشن
|
||||
</p>
|
||||
</div>
|
||||
<SunMoonToggle theme={initialTheme} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Menu Items */}
|
||||
<div className="flex flex-col gap-0 w-full bg-WHITE">
|
||||
{storeMenuItems.map((item, index) => (
|
||||
<div className="flex flex-col gap-0 w-full bg-WHITE lg:grid lg:grid-cols-3 lg:gap-4">
|
||||
{storeMenuItems.map((item, index) => {
|
||||
const isLogout = item.pageName === "logout";
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`flex flex-row gap-2 items-center justify-between px-[16px] active:bg-hover transition cursor-pointer ${
|
||||
item.hasBorder && "border-t-[2px]"
|
||||
} h-[55px] w-full`}
|
||||
} h-[55px] w-full lg:h-auto lg:px-0 lg:border-t-0 lg:border lg:border-WHITE3 lg:rounded-2xl lg:p-5 lg:items-center lg:hover:shadow-md lg:transition-shadow ${
|
||||
isLogout ? "lg:border-RED/40 lg:text-RED" : ""
|
||||
}`}
|
||||
onClick={() => handleItemClick(item)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
@ -200,12 +214,19 @@ export default function StoreSetting() {
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-row gap-2 items-center">
|
||||
<span
|
||||
className={`lg:w-11 lg:h-11 lg:rounded-xl lg:flex lg:items-center lg:justify-center ${
|
||||
isLogout ? "lg:bg-RED/10" : "lg:bg-WHITE2"
|
||||
}`}
|
||||
>
|
||||
{item.image}
|
||||
<p className={"text-R12"}>{item.title}</p>
|
||||
</span>
|
||||
<p className="text-R12 lg:text-B16 font-bold">{item.title}</p>
|
||||
</div>
|
||||
<ChevronLeft className="size-4" />
|
||||
<ChevronLeft className="size-4 lg:hidden" />
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Instagram Sync Drawer */}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user