feat(profile): mobile account hub redesign

Rework the mobile profile to match the design: an avatar + name + phone
header with an edit button, a wallet gradient card (live balance from
useServiceGetWallet + افزایش موجودی → /profile/wallet), a 4-up quick-action
grid (orders / bookmarks / following[badge] / addresses), a «حساب من»
section label, the theme toggle, and a trimmed menu list (items surfaced
above are dropped from the list). Desktop profile (sidebar + welcome
panel) is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Arda Samadi 2026-06-27 23:41:36 +03:30
parent 254fed4a99
commit 668649d540

View File

@ -15,9 +15,11 @@ import {
CircleUserRound,
FileQuestion,
Layers,
Pencil,
} from "lucide-react";
import { Link, useNavigate } from "@remix-run/react";
import { useServiceGetProfile } from "~/utils/RequestHandler";
import { useServiceGetWallet } from "~/requestHandler/use-wallet-hooks";
import { Skeleton } from "~/components/ui/skeleton";
import { useState } from "react";
import { Button } from "~/components/ui/button";
@ -53,12 +55,53 @@ export default function Profile() {
const [loginModalOpen, setLoginModalOpen] = useState(false);
const [logoutModalOpen, setLogoutModalOpen] = useState(false);
const { data: profileData } = useServiceGetProfile();
const { data: walletData } = useServiceGetWallet();
const { data: followedSellers, isLoading: isFollowedSellersLoading } =
useServiceGetFollowedSellers();
// Calculate the count of followed sellers
const followedCount = followedSellers?.length || 0;
const walletBalance = walletData?.balance
? new Intl.NumberFormat("fa-IR").format(parseInt(walletData.balance))
: "۰";
// Top quick-actions (also live in the menu, but surfaced as a grid)
const quickActions = [
{
label: "سفارش‌ها",
link: "/profile/orders",
icon: <ShoppingBag size={22} />,
badge: null as number | null,
},
{
label: "نشان‌ها",
link: "/profile/bookmarks",
icon: <BookmarkMinus size={22} />,
badge: null,
},
{
label: "دنبال‌ها",
link: "/profile/following",
icon: <CircleFadingPlus size={22} />,
badge: followedCount || null,
},
{
label: "آدرس‌ها",
link: "/profile/location",
icon: <MapPin size={22} />,
badge: null,
},
];
// The menu list below drops the items surfaced as quick actions / wallet card.
const quickPages = new Set([
"orders",
"bookmarks",
"following",
"location",
"wallet",
]);
// Create dynamic items with the followed count
const menuItems: MenuItemType[] = [
{
@ -147,21 +190,80 @@ export default function Profile() {
<p className="text-GRAY">یک گزینه را از منوی کناری انتخاب کنید</p>
</div>
{/* Mobile: account menu */}
{/* Mobile: account hub */}
<div className="flex flex-col gap-0 lg:hidden">
<ProfileTopBar user={user} />
<div
className={`flex flex-row h-[55px] w-full items-center gap-2 justify-between px-[16px] active:bg-hover transition`}
{/* Header: avatar + name + phone + edit */}
<div className="flex items-center gap-3.5 px-4 pt-4 pb-3">
<CircleUserRound size={56} className="text-GRAY shrink-0" />
<div className="min-w-0 flex-1">
<h2 className="text-B16 font-bold truncate">
{profileData?.username || "کاربر ویترون"}
</h2>
{profileData?.phoneNumber ? (
<p className="text-GRAY text-R12 mt-0.5" dir="ltr">
{profileData.phoneNumber}
</p>
) : null}
</div>
<Link
to="/profile/setting"
aria-label="ویرایش"
className="w-10 h-10 rounded-xl border border-WHITE3 grid place-items-center text-BLACK shrink-0"
>
<div className="flex flex-row gap-2 items-center">
<p className={"text-R12"}>
<Pencil size={18} />
</Link>
</div>
{/* Wallet card */}
<Link
to="/profile/wallet"
className="mx-4 mt-1 rounded-[18px] p-5 bg-gradient-to-br from-[#14213d] to-VITROWN_BLUE text-white flex items-center"
>
<div className="flex-1 min-w-0">
<small className="opacity-80 text-R12">موجودی کیف پول</small>
<b className="block text-[22px] font-extrabold mt-1">
{walletBalance}
<span className="text-[13px] font-semibold"> تومان</span>
</b>
</div>
<span className="bg-white text-BLACK rounded-xl px-4 py-2.5 text-R12 font-bold shrink-0">
افزایش موجودی
</span>
</Link>
{/* Quick actions */}
<div className="grid grid-cols-4 gap-2.5 px-4 pt-5 pb-1">
{quickActions.map((qa) => (
<Link
key={qa.link}
to={qa.link}
className="flex flex-col items-center gap-1.5"
>
<div className="relative w-[52px] h-[52px] rounded-2xl bg-WHITE2 grid place-items-center text-BLACK">
{qa.badge ? (
<span className="absolute -top-1 -left-1 min-w-[18px] h-[18px] px-1 rounded-full bg-RED text-white text-[10px] font-bold grid place-items-center border-2 border-WHITE">
{qa.badge}
</span>
) : null}
{qa.icon}
</div>
<span className="text-R12 text-BLACK2">{qa.label}</span>
</Link>
))}
</div>
<p className="text-R12 font-bold text-GRAY px-4 mt-4 mb-1">حساب من</p>
{/* Theme toggle */}
<div className="flex flex-row h-[55px] w-full items-center gap-2 justify-between px-[16px]">
<p className="text-R12">
{initialTheme === "dark" ? "حالت تاریک" : "حالت روشن"}
</p>
</div>
<SunMoonToggle theme={initialTheme} />
</div>
<MenuItems
items={menuItems}
items={menuItems.filter((m) => !quickPages.has(m.pageName))}
onStoreClick={() => {
if (profileData?.sellerUsername) {
navigate(`/store/${profileData.sellerUsername}`);