feat: improve login/logout flow in customer SPA

1. Logout flow:
   - Added confirmation dialog (window.confirm)
   - Changed to API-based logout (/auth/logout)
   - Full page reload after logout to clear cookies
   - Added loading state during logout

2. Login flow (already correct):
   - Uses window.location.href for full page redirect
   - Redirects to /store/#/my-account after login
This commit is contained in:
Dwindi Ramadhana
2026-01-01 17:00:11 +07:00
parent 58681e272e
commit c83ea78911

View File

@@ -1,4 +1,4 @@
import React, { ReactNode } from 'react'; import React, { ReactNode, useState } from 'react';
import { Link, useLocation } from 'react-router-dom'; import { Link, useLocation } from 'react-router-dom';
import { LayoutDashboard, ShoppingBag, Download, MapPin, Heart, User, LogOut } from 'lucide-react'; import { LayoutDashboard, ShoppingBag, Download, MapPin, Heart, User, LogOut } from 'lucide-react';
import { useModules } from '@/hooks/useModules'; import { useModules } from '@/hooks/useModules';
@@ -12,7 +12,8 @@ export function AccountLayout({ children }: AccountLayoutProps) {
const user = (window as any).woonoowCustomer?.user; const user = (window as any).woonoowCustomer?.user;
const { isEnabled } = useModules(); const { isEnabled } = useModules();
const wishlistEnabled = (window as any).woonoowCustomer?.settings?.wishlist_enabled !== false; const wishlistEnabled = (window as any).woonoowCustomer?.settings?.wishlist_enabled !== false;
const [isLoggingOut, setIsLoggingOut] = useState(false);
const allMenuItems = [ const allMenuItems = [
{ id: 'dashboard', label: 'Dashboard', path: '/my-account', icon: LayoutDashboard }, { id: 'dashboard', label: 'Dashboard', path: '/my-account', icon: LayoutDashboard },
{ id: 'orders', label: 'Orders', path: '/my-account/orders', icon: ShoppingBag }, { id: 'orders', label: 'Orders', path: '/my-account/orders', icon: ShoppingBag },
@@ -21,14 +22,33 @@ export function AccountLayout({ children }: AccountLayoutProps) {
{ id: 'wishlist', label: 'Wishlist', path: '/my-account/wishlist', icon: Heart }, { id: 'wishlist', label: 'Wishlist', path: '/my-account/wishlist', icon: Heart },
{ id: 'account-details', label: 'Account Details', path: '/my-account/account-details', icon: User }, { id: 'account-details', label: 'Account Details', path: '/my-account/account-details', icon: User },
]; ];
// Filter out wishlist if module disabled or settings disabled // Filter out wishlist if module disabled or settings disabled
const menuItems = allMenuItems.filter(item => const menuItems = allMenuItems.filter(item =>
item.id !== 'wishlist' || (isEnabled('wishlist') && wishlistEnabled) item.id !== 'wishlist' || (isEnabled('wishlist') && wishlistEnabled)
); );
const handleLogout = () => { const handleLogout = async () => {
window.location.href = '/wp-login.php?action=logout'; setIsLoggingOut(true);
try {
const apiRoot = (window as any).woonoowCustomer?.apiRoot || '/wp-json/woonoow/v1';
const nonce = (window as any).woonoowCustomer?.nonce || '';
await fetch(`${apiRoot}/auth/logout`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': nonce,
},
credentials: 'include',
});
// Full page reload to clear cookies and refresh state
window.location.href = window.location.origin + '/store/';
} catch (error) {
// Even on error, try to redirect and let server handle session
window.location.href = window.location.origin + '/store/';
}
}; };
const isActive = (path: string) => { const isActive = (path: string) => {
@@ -52,7 +72,7 @@ export function AccountLayout({ children }: AccountLayoutProps) {
</div> </div>
</div> </div>
</div> </div>
<nav className="space-y-1"> <nav className="space-y-1">
{menuItems.map((item) => { {menuItems.map((item) => {
const Icon = item.icon; const Icon = item.icon;
@@ -60,24 +80,28 @@ export function AccountLayout({ children }: AccountLayoutProps) {
<Link <Link
key={item.id} key={item.id}
to={item.path} to={item.path}
className={`flex items-center gap-3 px-4 py-2.5 rounded-lg transition-colors ${ className={`flex items-center gap-3 px-4 py-2.5 rounded-lg transition-colors ${isActive(item.path)
isActive(item.path) ? 'bg-primary text-primary-foreground'
? 'bg-primary text-primary-foreground' : 'text-gray-700 hover:bg-gray-100'
: 'text-gray-700 hover:bg-gray-100' }`}
}`}
> >
<Icon className="w-5 h-5" /> <Icon className="w-5 h-5" />
<span className="font-medium">{item.label}</span> <span className="font-medium">{item.label}</span>
</Link> </Link>
); );
})} })}
<button <button
onClick={handleLogout} onClick={() => {
className="w-full font-[inherit] flex items-center gap-3 px-4 py-2.5 rounded-lg text-gray-700 hover:bg-gray-100 transition-colors" if (window.confirm('Are you sure you want to log out?')) {
handleLogout();
}
}}
disabled={isLoggingOut}
className="w-full font-[inherit] flex items-center gap-3 px-4 py-2.5 rounded-lg text-gray-700 hover:bg-gray-100 transition-colors disabled:opacity-50"
> >
<LogOut className="w-5 h-5" /> <LogOut className="w-5 h-5" />
<span className="font-medium">Logout</span> <span className="font-medium">{isLoggingOut ? 'Logging out...' : 'Logout'}</span>
</button> </button>
</nav> </nav>
</aside> </aside>
@@ -93,11 +117,10 @@ export function AccountLayout({ children }: AccountLayoutProps) {
<Link <Link
key={item.id} key={item.id}
to={item.path} to={item.path}
className={`flex items-center gap-2 px-6 py-4 border-b-2 transition-colors whitespace-nowrap text-sm ${ className={`flex items-center gap-2 px-6 py-4 border-b-2 transition-colors whitespace-nowrap text-sm ${isActive(item.path)
isActive(item.path) ? 'border-primary text-primary font-medium'
? 'border-primary text-primary font-medium' : 'border-transparent text-gray-600 hover:text-gray-900'
: 'border-transparent text-gray-600 hover:text-gray-900' }`}
}`}
> >
<Icon className="w-5 h-5" /> <Icon className="w-5 h-5" />
<span>{item.label}</span> <span>{item.label}</span>
@@ -113,7 +136,7 @@ export function AccountLayout({ children }: AccountLayoutProps) {
<div className="py-8"> <div className="py-8">
{/* Mobile: Tab Navigation */} {/* Mobile: Tab Navigation */}
<TabNav /> <TabNav />
{/* Desktop: Sidebar + Content */} {/* Desktop: Sidebar + Content */}
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-4 gap-6">
<div className="hidden lg:block lg:col-span-1"> <div className="hidden lg:block lg:col-span-1">