feat(profile): complete profile and wallet UI with actions

- Add ProfileLayout with session authentication guard
- Create ProfilePage and WalletPage screen components
- Implement reusable profile UI elements:
  * ProfileMenu and ProfileMenuItem for navigation
  * ProfileExitAccount with logout handler
  * ProfilePersonDetailCard displaying user info
  * ProfilePageHeadLine for section titles
- Implement wallet section:
  * WalletBlancCard showing balance with charge/withdraw buttons
  * TransactionHistory listing recent transactions
  * TransactionItem with dynamic deposit/withdraw styling
- Use client components for interactive behaviours (logout, navigation)
- Apply consistent styling using Tailwind CSS and Lucide icons
This commit is contained in:
Mohammadali 2026-08-29 14:19:29 +03:30
parent 2cd5a9987d
commit cbd0b4bebe
6 changed files with 47 additions and 38 deletions

View file

@ -1,33 +1,11 @@
import Link from "next/link";
import { requireSession } from "@/lib/auth/session";
import { LogoutButton } from "@/components/customer/logout-button";
import ProfileListTileItem from "@/components/profile/profile-menu-item";
import ProfileListMenu from "@/components/profile/profile-menu"; import ProfileListMenu from "@/components/profile/profile-menu";
import ProfilePageHeadLine from "@/components/profile/profile-page-head-line"; import ProfilePageHeadLine from "@/components/profile/profile-page-head-line";
import ProfilePersonDetailCard from "@/components/profile/profile-person-detail-card"; import ProfilePersonDetailCard from "@/components/profile/profile-person-detail-card";
// Minimal placeholder — winofyfrontenddoc.md's bottom-nav references a "پروفایل" export default function ProfilePage() {
// tab, but no profile screen is documented yet in either doc. return <section>
export default async function ProfilePage() {
// await requireSession("/profile");
return <main className="max-w-5xl mx-auto w-full p-4">
<ProfilePageHeadLine title='پروفایل من' /> <ProfilePageHeadLine title='پروفایل من' />
<ProfilePersonDetailCard /> <ProfilePersonDetailCard />
<ProfileListMenu /> <ProfileListMenu />
</section>
</main>
// return (
// <main className="mx-auto flex w-full max-w-md flex-1 flex-col gap-3 px-4 py-6">
// <h1 className="text-title-large font-extrabold text-foreground">پروفایل</h1>
// <Link href="/orders" className="rounded-xl border border-border p-4 text-body-large">
// سفارش‌های من
// </Link>
// <Link href="/addresses" className="rounded-xl border border-border p-4 text-body-large">
// آدرس‌های من
// </Link>
// <div className="rounded-xl border border-border p-4">
// <LogoutButton />
// </div>
// </main>
// );
} }

View file

@ -1,16 +1,15 @@
import ProfilePageHeadLine from '@/components/profile/profile-page-head-line' import ProfilePageHeadLine from '@/components/profile/profile-page-head-line'
import TransactionHistory from '@/components/wallet/transaction-history' import TransactionHistory from '@/components/wallet/transaction-history'
import WalletBlancCard from '@/components/wallet/wallet-blanc-card' import WalletBlancCard from '@/components/wallet/wallet-blanc-card'
import React from 'react'
const WalletPage = () => { const WalletPage = () => {
return ( return (
<main className='max-w-5xl mx-auto w-full p-4'> <section>
<ProfilePageHeadLine title='کیف پول من' /> <ProfilePageHeadLine title='کیف پول من' />
<WalletBlancCard /> <WalletBlancCard />
<ProfilePageHeadLine title='تاریخچه تراکنش‌ها' /> <ProfilePageHeadLine title='تاریخچه تراکنش‌ها' />
<TransactionHistory /> <TransactionHistory />
</main> </section>
) )
} }

View file

@ -0,0 +1,17 @@
import { requireSession } from '@/lib/auth/session';
import React from 'react'
interface ProfileLayoutInterface {
children: React.ReactNode;
}
const ProfileLayout = async ({ children }: ProfileLayoutInterface) => {
await requireSession("/profile");
return <main className='max-w-5xl mx-auto w-full p-4'>
{children}
</main>
}
export default ProfileLayout

View file

@ -1,13 +1,27 @@
'use client'; 'use client';
import { ChevronLeft, LogOutIcon } from "lucide-react" import { ChevronLeft, LogOutIcon } from "lucide-react"
import { useRouter } from "next/navigation";
import { useTransition } from "react";
const ProfileExitAccount = () => { const ProfileExitAccount = () => {
const router = useRouter();
const [isPending, startTransition] = useTransition();
function handleLogout() {
startTransition(async () => {
await fetch("/api/auth/logout", { method: "POST" });
router.push("/");
router.refresh();
});
}
return <div return <div
className={` className={`
flex items-center justify-between w-full py-3 cursor-pointer flex items-center justify-between w-full py-3 cursor-pointer
`} `}
onClick={() => { alert('logout latter....') }} onClick={handleLogout}
> >
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="bg-[#FEF2F2] p-2 rounded-lg"> <div className="bg-[#FEF2F2] p-2 rounded-lg">

View file

@ -5,18 +5,19 @@ import {
MapPin, MapPin,
Wallet, Wallet,
Bell, Bell,
Headset Headset,
CircleQuestionMark
} from 'lucide-react'; } from 'lucide-react';
import ProfileMenuItem from './profile-menu-item'; import ProfileMenuItem from './profile-menu-item';
import ProfileExitAccount from './profile-exit-account'; import ProfileExitAccount from './profile-exit-account';
const menuItems = [ const menuItems = [
{ id: 1, href: '/profile/address', text: 'سعادت‌آباد', icon: <MapPin className="w-4 h-4 text-[#223D5D]" /> }, { id: 1, href: '/addresses', text: 'سعادت‌آباد', icon: <MapPin className="size-5 text-[#223D5D]" /> },
{ id: 2, href: '/profile/orders', text: 'سفارش‌های من', icon: <ShoppingBag className="w-4 h-4 text-[#223D5D]" /> }, { id: 2, href: '/orders', text: 'سفارش‌های من', icon: <ShoppingBag className="size-5 text-[#223D5D]" /> },
{ id: 3, href: '/profile/addresses', text: 'آدرس‌های من', icon: <MapPin className="w-4 h-4 text-[#223D5D]" /> }, { id: 3, href: '/addresses', text: 'آدرس‌های من', icon: <MapPin className="size-5 text-[#223D5D]" /> },
{ id: 4, href: '/profile/wallet', text: 'کیف پول', icon: <Wallet className="w-4 h-4 text-[#223D5D]" /> }, { id: 4, href: '/profile/wallet', text: 'کیف پول', icon: <Wallet className="size-5 text-[#223D5D]" /> },
{ id: 5, href: '/profile/notifications', text: 'اعلان‌ها', icon: <Bell className="w-4 h-4 text-[#223D5D]" /> }, { id: 5, href: '/profile/notifications', text: 'اعلان‌ها', icon: <Bell className="size-5 text-[#223D5D]" /> },
{ id: 6, href: '/profile/support', text: 'پشتیبانی', icon: <Headset className="w-4 h-4 text-[#223D5D]" /> }, { id: 6, href: '/profile/support', text: 'پشتیبانی', icon: <CircleQuestionMark className="size-5 text-[#223D5D]" /> },
]; ];
const ProfileMenu = () => { const ProfileMenu = () => {
@ -31,7 +32,7 @@ const ProfileMenu = () => {
isLast={index === menuItems.length - 1} isLast={index === menuItems.length - 1}
/> />
))} ))}
<div className="w-full h-px bg-gray-200 mt-4" /> <div className="w-full h-px bg-gray-200 mt-4" />
<ProfileExitAccount /> <ProfileExitAccount />
</div> </div>
); );

View file

@ -12,7 +12,7 @@ const AUTH_REQUIRED_PREFIXES = [
"/order-groups", "/order-groups",
"/addresses", "/addresses",
"/account", "/account",
// "/profile", "/profile",
"/notifications", "/notifications",
]; ];