Vitron-Front/app/routes/store.$storeId.setting.tsx
Arda Samadi 257871d5f1 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>
2026-06-17 13:00:18 +03:30

280 lines
9.9 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { MetaFunction, useNavigate, useParams } from "@remix-run/react";
import { useSellerData } from "../requestHandler/use-seller-hooks";
import {
ChevronLeft,
LogOut,
LayoutDashboard,
ShoppingCart,
Truck,
PackagePlus,
TableProperties,
PenLine,
Code,
Instagram,
MessageCircle,
} from "lucide-react";
import HeaderWithSupport from "../components/HeaderWithSupport";
import { useStoreOwnership } from "../hooks/useStoreOwnership";
import { useState } from "react";
import { SunMoonToggle } from "~/components/SunMoonToggle";
import { useRootData } from "~/hooks/use-root-data";
// Import the InstagramSyncDrawer component
import { InstagramSyncDrawer } from "./store.$storeId._index";
import CallDialog from "~/components/CallDialog";
interface MenuItemType {
title: string;
link?: string;
pageName: string;
hasBorder?: boolean;
image: React.ReactNode;
}
export default function StoreSetting() {
const { storeId } = useParams();
const navigate = useNavigate();
const { data } = useSellerData(storeId);
const { theme: initialTheme } = useRootData();
// Check store ownership - this will redirect if user doesn't own the store
const { isLoading: isOwnershipLoading } = useStoreOwnership(storeId);
// State for Instagram sync drawer
const [instagramSyncDrawerOpen, setInstagramSyncDrawerOpen] = useState(false);
const [isCallModalOpen, setIsCallModalOpen] = useState(false);
// Show loading while checking ownership
if (isOwnershipLoading) {
return (
<div className="flex items-center justify-center min-h-[500px]">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
</div>
);
}
// Menu items for store management
const storeMenuItems: MenuItemType[] = [
{
title: "وارد کردن پست های اینستاگرام",
pageName: "instagram-sync",
hasBorder: true,
image: <Instagram size={20} />,
},
{
title: "سفارش‌های دریافتی",
pageName: "seller-orders",
link: `/store/${storeId}/orders`,
image: <ShoppingCart size={20} />,
},
{
title: "پیام های دریافتی",
pageName: "seller-messages",
link: `/store/${storeId}/threads`,
image: <MessageCircle size={20} />,
},
{
title: "داشبورد مالی",
pageName: "financial-dashboard",
link: `/store/${storeId}/financial-dashboard`,
image: <LayoutDashboard size={20} />,
},
{
title: "تعیین نحوه ارسال",
pageName: "shipping-method",
link: `/store/${storeId}/shipping-method`,
image: <Truck size={20} />,
},
{
title: "افزودن محصول جدید",
pageName: "add-product",
link: `/store/add/manual/${storeId}`,
image: <PackagePlus size={20} />,
},
{
title: "لیست محصولات",
pageName: "products-list",
link: `/store/${storeId}/products`,
image: <TableProperties size={20} />,
},
{
title: "ویرایش اطلاعات فروشگاه",
pageName: "edit-store",
link: `/store/${storeId}/edit`,
image: <PenLine size={20} />,
},
{
title: "خروج",
pageName: "logout",
link: `/store/${storeId}/logout`,
hasBorder: true,
image: <LogOut size={20} />,
},
];
const handleBack = () => {
navigate(`/store/${storeId}`);
};
const handleItemClick = (item: MenuItemType) => {
// Special handling for "instagram-sync" item
if (item.pageName === "instagram-sync") {
// Open Instagram sync drawer instead of navigating
setInstagramSyncDrawerOpen(true);
return;
}
// Special handling for "logout" item
if (item.pageName === "logout") {
// Handle logout logic
navigate("/");
return;
}
// Default rendering for other items with Link if they have a link
if (item.link) {
navigate(item.link);
return;
}
};
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 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 lg:text-B16 font-bold">
وضعیت فروشگاه (فعال)
</p>
</div>
<p className="text-R10 lg:text-R12 text-GRAY">
در حالت فعال، فروشگاه برای مخاطبان نمایش داده میشود.
</p>
</div>
<label className="relative inline-flex items-center cursor-pointer">
<input
type="checkbox"
checked={data?.isVerified || false}
className="sr-only peer"
aria-label="وضعیت فروشگاه"
/>
<div className="w-11 h-6 bg-WHITE2 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-WHITE after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-WHITE after:border-GRAY3 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-VITROWN_BLUE border"></div>
</label>
</div>
</div>
{/* Theme Toggle */}
<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 lg:text-B16 font-bold">
{initialTheme === "dark" ? "حالت تاریک" : "حالت روشن"}
</p>
<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 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 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}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
handleItemClick(item);
}
}}
>
<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}
</span>
<p className="text-R12 lg:text-B16 font-bold">{item.title}</p>
</div>
<ChevronLeft className="size-4 lg:hidden" />
</div>
);
})}
</div>
{/* Instagram Sync Drawer */}
<InstagramSyncDrawer
setIsCallModalOpen={setIsCallModalOpen}
isOpen={instagramSyncDrawerOpen}
onOpenChange={setInstagramSyncDrawerOpen}
storeId={storeId || ""}
/>
<CallDialog
isCallModalOpen={isCallModalOpen}
setIsCallModalOpen={setIsCallModalOpen}
handleCallConfirm={() => {
window.location.href = `tel:${import.meta.env.VITE_SUPPORT_PHONE}`;
}}
/>
</div>
);
}
export const meta: MetaFunction = () => {
return [
{ title: "تنظیمات فروشگاه - ویترون" },
{
name: "description",
content:
"مدیریت و مشاهده تمام تنظیمات فروشگاه در فروشگاه شما. فیلتر بر اساس وضعیت و جستجو در تنظیمات فروشگاه.",
},
{
name: "keywords",
content:
"تنظیمات فروشگاه، مدیریت تنظیمات فروشگاه، فروشگاه، مشتری، ویترون",
},
{ name: "robots", content: "noindex, follow" },
{ property: "og:title", content: "تنظیمات فروشگاه - ویترون" },
{
property: "og:description",
content:
"مدیریت و مشاهده تمام تنظیمات فروشگاه در فروشگاه شما. فیلتر بر اساس وضعیت و جستجو در تنظیمات فروشگاه.",
},
{ property: "og:type", content: "website" },
{ name: "twitter:card", content: "summary" },
{ name: "twitter:title", content: "تنظیمات فروشگاه - ویترون" },
{
name: "twitter:description",
content:
"مدیریت و مشاهده تمام تنظیمات فروشگاه در فروشگاه شما. فیلتر بر اساس وضعیت و جستجو در تنظیمات فروشگاه.",
},
];
};