import React, { Suspense, lazy, useState, useEffect, useMemo, useCallback } from 'react'; import { useDesignTokens } from '@/theme/useDesignTokens'; import { useQuery } from '@tanstack/react-query'; import { usePullToRefresh } from '@/hooks/usePullToRefresh'; import { PullToRefreshIndicator } from '@/components/ui/PullToRefreshIndicator'; import { ShoppingBagIcon, FunnelIcon, MagnifyingGlassIcon, AdjustmentsHorizontalIcon, CurrencyDollarIcon, TagIcon, SparklesIcon, ShoppingCartIcon, HeartIcon, PlusIcon, ClipboardDocumentListIcon, ChartBarIcon, BanknotesIcon, ExclamationTriangleIcon, CalendarIcon, CreditCardIcon, EyeIcon, UserIcon, Bars3Icon, } from '@heroicons/react/24/outline'; import { ShoppingBagIcon as ShoppingBagSolidIcon, ShoppingCartIcon as ShoppingCartSolidIcon, HeartIcon as HeartSolidIcon, } from '@heroicons/react/24/solid'; // Components import { Input } from '../../components/ui/design-system/Input'; import { Button } from '../../components/ui/design-system/Button'; import { Dropdown } from '../../components/ui/design-system/patterns/Dropdown'; import { LoadingSpinner } from '../../components/ui/LoadingSpinner'; import { PinCard } from '../../components/ui/PinCard'; import { MarketplaceFilters } from './components/MarketplaceFilters'; import { MarketplaceSkeleton } from './components/MarketplaceSkeleton'; import { PinDetailModal } from '../../components/ui/PinDetailModal'; import { SellerOnboardingBanner } from './components/SellerOnboardingBanner'; // Lazy load heavy modals and checkout components const WishlistModal = lazy(() => import('../shopping/components/WishlistModal').then((m) => ({ default: m.WishlistModal })) ); const CheckoutModal = lazy(() => import('../shopping/components/CheckoutModal').then((m) => ({ default: m.CheckoutModal })) ); const AddPinModal = lazy(() => import('../pins/components/AddPinModal').then((m) => ({ default: m.AddPinModal })) ); const PurchaseModal = lazy(() => import('./components/PurchaseModal').then((m) => ({ default: m.PurchaseModal })) ); import { MarketplacePinCard } from './components/MarketplacePinCard'; import { useToast } from '@/hooks/useToast'; import { useTranslation } from '@/hooks/useTranslation'; import { LoginModal } from '../../components/ui/LoginModal'; import { useSidebar } from '@/contexts/SidebarContext'; import { MarketplaceSearch } from './components/MarketplaceSearch'; import { MarketplaceGrid } from './components/MarketplaceGrid'; import { SourceSelector } from './components/SourceSelector'; import { EbayItemCard } from './components/EbayItemCard'; import { CategoryNavigation } from './components/CategoryNavigation'; import { SellerNavigation, type SellerTab } from './components/SellerNavigation'; // Lazy load heavy components const FilterBar = lazy(() => import('./components/FilterBar').then((m) => ({ default: m.FilterBar })) ); const SellerOverview = lazy(() => import('./components/SellerOverview').then((m) => ({ default: m.SellerOverview })) ); const MarketplaceHomepage = lazy(() => import('./components/MarketplaceHomepage').then((m) => ({ default: m.MarketplaceHomepage })) ); const MarketplaceToolbar = lazy(() => import('./components/MarketplaceToolbar').then((m) => ({ default: m.MarketplaceToolbar })) ); const SellerListingsTab = lazy(() => import('../seller/components/SellerListingsTab').then((m) => ({ default: m.SellerListingsTab })) ); const SellerOrdersTab = lazy(() => import('../seller/components/SellerOrdersTab').then((m) => ({ default: m.SellerOrdersTab })) ); const SellerPayoutsTab = lazy(() => import('../seller/components/SellerPayoutsTab').then((m) => ({ default: m.SellerPayoutsTab })) ); const VendorsTab = lazy(() => import('./components/VendorsTab').then((m) => ({ default: m.VendorsTab })) ); import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/design-system/Tabs'; // Services & Types import marketplaceService from '../../services/marketplaceService'; import { ebayService } from '../../services/ebayService'; import { pinsService } from '../../services/pinsService'; import type { MarketplaceFilters as FiltersType, MarketplacePin } from '../../types/marketplace'; import type { Pin } from '../../types/pin'; import type { MarketplaceSource } from '../../types/ebay'; import { useAuthStore } from '../../store/authStore'; // Removed duplicate import of useToast import { useNavigate, useSearchParams } from 'react-router-dom'; import { useTheme } from '@/theme/ThemeProvider'; import { useLoginModal } from '../../hooks/useLoginModal'; import { getAllCategories } from '@/constants/categories'; // Constants const ITEMS_LIMIT = 100; // Load more items at once like in my-pins const SORT_OPTIONS = [ { value: 'price_low', label: 'Price: Low to High' }, { value: 'price_high', label: 'Price: High to Low' }, { value: 'newest', label: 'Newest First' }, { value: 'popular', label: 'Most Popular' }, { value: 'name_asc', label: 'Name: A-Z' }, { value: 'name_desc', label: 'Name: Z-A' }, ]; export const MarketplacePage: React.FC = () => { const { user, isAuthenticated } = useAuthStore(); const { showToast } = useToast(); const { semantic, shadows } = useDesignTokens(); const navigate = useNavigate(); const { actualTheme } = useTheme(); const { showLoginModal, hideLoginModal, isOpen: isLoginModalOpen } = useLoginModal(); const { setForceCollapsed } = useSidebar(); const [urlSearchParams, setUrlSearchParams] = useSearchParams(); // State const [activeTab, setActiveTab] = useState<'browse' | 'seller'>('browse'); const [directory, setDirectory] = useState<'pins' | 'vendors'>('pins'); const [isMobile, setIsMobile] = useState(window.innerWidth < 1024); const [sellerTab, setSellerTab] = useState('overview'); const [searchQuery, setSearchQuery] = useState(''); const [source, setSource] = useState('all'); const [filters, setFilters] = useState({ limit: ITEMS_LIMIT, offset: 0, sortBy: 'best_match', deliveryMethod: 'all', listType: 'all', condition: 'all', dateListed: 'all', }); const [showFilters, setShowFilters] = useState(false); const [selectedPin, setSelectedPin] = useState(null); const [checkoutPin, setCheckoutPin] = useState(null); const [purchasePin, setPurchasePin] = useState(null); const [showPurchaseModal, setShowPurchaseModal] = useState(false); const [showWishlist, setShowWishlist] = useState(false); const [focusOnComment, setFocusOnComment] = useState(false); const [showCreatePinModal, setShowCreatePinModal] = useState(false); // Removed currentPage state - no pagination needed const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [selectedCategory, setSelectedCategory] = useState(); const [selectedSubcategory, setSelectedSubcategory] = useState(); const [distance, setDistance] = useState('15'); const [sortBy, setSortBy] = useState('most_relevant'); const [showHomepage, setShowHomepage] = useState(true); const { marketplace: tMkt, common: tCommon, pins: tPins, auth: tAuth } = useTranslation(); const lazyFallback = (
); // ===== URL Helpers ===== const setParam = (params: URLSearchParams, key: string, value?: string | number | null) => { if (value === undefined || value === null || value === '' || value === 'all') { params.delete(key); } else { params.set(key, String(value)); } }; const updateUrl = (updates: Partial>) => { setUrlSearchParams( (prev) => { const next = new URLSearchParams(prev); Object.entries(updates).forEach(([k, v]) => setParam(next, k, v)); return next; }, { replace: true } ); }; // ===== Hydrate state from URL on mount ===== useEffect(() => { const tabParam = urlSearchParams.get('tab'); const directoryParam = urlSearchParams.get('directory'); const sellerTabParam = urlSearchParams.get('sellerTab') as SellerTab | null; const sourceParam = (urlSearchParams.get('source') as MarketplaceSource) || 'all'; const qParam = urlSearchParams.get('q') || ''; const categoryParam = urlSearchParams.get('category') || undefined; const subcategoryParam = urlSearchParams.get('subcategory') || undefined; const listTypeParam = (urlSearchParams.get('listType') as FiltersType['listType']) || 'all'; const conditionParam = (urlSearchParams.get('condition') as FiltersType['condition']) || 'all'; const dateListedParam = (urlSearchParams.get('dateListed') as FiltersType['dateListed']) || 'all'; const sortByParam = urlSearchParams.get('sortBy') || 'best_match'; const minPriceParam = urlSearchParams.get('minPrice'); const maxPriceParam = urlSearchParams.get('maxPrice'); if (directoryParam === 'vendors') { setDirectory('vendors'); } // Back-compat: tab=vendors used to exist; map it to browse + vendors directory if (tabParam === 'seller') { setActiveTab(user ? 'seller' : 'browse'); } else if (tabParam === 'browse') { setActiveTab('browse'); } else if (tabParam === 'vendors') { setActiveTab('browse'); setDirectory('vendors'); } if (sellerTabParam) setSellerTab(sellerTabParam); if (sourceParam) setSource(sourceParam); if (qParam) { setSearchQuery(qParam); setShowHomepage(false); } if (categoryParam) setSelectedCategory(categoryParam); if (subcategoryParam) setSelectedSubcategory(subcategoryParam); setFilters((prev) => ({ ...prev, search: qParam || undefined, category: categoryParam || undefined, subcategory: subcategoryParam || undefined, listType: listTypeParam, condition: conditionParam, dateListed: dateListedParam, sortBy: (sortByParam as any) || 'best_match', minPrice: minPriceParam ? Number(minPriceParam) : undefined, maxPrice: maxPriceParam ? Number(maxPriceParam) : undefined, offset: 0, })); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // Queries - Use unified search for all sources (all, pinpal, ebay) const shouldUseUnifiedSearch = source === 'all' || source === 'ebay' || source === 'pinpal'; const { data: pinsData, isLoading: isLoadingPins, error: pinsError, refetch: refetchPins, } = useQuery({ queryKey: ['marketplace-pins', filters, source, searchQuery], queryFn: async () => { // 🎯 SPECIAL HANDLING FOR ISO PINS: Show user ISO pins when ISO filter is active if (filters.listType === 'iso') { console.log(' Loading ISO pins for marketplace wishlist view...'); try { // Load all ISO pins from all users (public wanted listings) const response = await marketplaceService.getMarketplacePins({ ...filters, listType: 'iso', // Ensure we're filtering for ISO limit: filters.limit || ITEMS_LIMIT, }); return { success: true, data: { pins: response.data?.pins || [], total: response.data?.total || 0, sources: { pinpal: { count: response.data?.total || 0 }, ebay: { count: 0 }, // No eBay ISO listings }, }, }; } catch (error) { console.error(' Error loading ISO pins:', error); return { success: false, data: { pins: [], total: 0, sources: {} }, }; } } if (shouldUseUnifiedSearch) { // Use unified search - permitir busca vazia (Pinpal Only pode listar sem termo) const queryToUse = searchQuery.trim(); // Map sortBy values to eBay API format const sortByMapping: Record = { best_match: 'relevance', price_low: 'price-asc', price_high: 'price-desc', newest: 'newest', }; const unifiedResults = await ebayService.searchUnified({ query: queryToUse, source, // 'all' | 'pinpal' | 'ebay' limit: filters.limit, offset: filters.offset, sort: sortByMapping[filters.sortBy || 'best_match'] as | 'relevance' | 'price-asc' | 'price-desc' | 'newest', minPrice: filters.minPrice, maxPrice: filters.maxPrice, condition: filters.condition === 'all' ? undefined : filters.condition, category: filters.category === 'all' ? undefined : filters.category, }); // Track returned items count (best-effort) try { const { analyticsService } = await import('@/services/analyticsService'); analyticsService.trackEvent({ event: 'search', category: 'marketplace', action: 'unified', label: queryToUse, value: (unifiedResults?.data?.items?.length ?? 0) as number, metadata: { source, items: unifiedResults?.data?.items?.length ?? 0 }, }); } catch (_) {} return { success: true, data: { pins: unifiedResults.data.items || [], total: unifiedResults.data.total || 0, sources: unifiedResults.data.sources, }, }; } else { // Use regular Pinpal search return marketplaceService.getMarketplacePins(filters); } }, staleTime: 5 * 60 * 1000, // 5 minutes enabled: true, // Always enable the query }); const { data: categoriesData } = useQuery({ queryKey: ['marketplace-categories'], queryFn: () => marketplaceService.getMarketplaceCategories(), staleTime: 30 * 60 * 1000, // 30 minutes }); const { data: raritiesData } = useQuery({ queryKey: ['marketplace-rarities'], queryFn: () => marketplaceService.getMarketplaceRarities(), staleTime: 30 * 60 * 1000, // 30 minutes }); const { data: conditionsData } = useQuery({ queryKey: ['marketplace-conditions'], queryFn: () => marketplaceService.getMarketplaceConditions(), staleTime: 30 * 60 * 1000, // 30 minutes }); const { data: priceRangeData } = useQuery({ queryKey: ['marketplace-price-range'], queryFn: () => marketplaceService.getMarketplacePriceRange(), staleTime: 10 * 60 * 1000, // 10 minutes }); // Computed values const pins = pinsData?.data?.pins || []; const totalPins = pinsData?.data?.total || 0; // Removed pagination - load all items at once like in my-pins const categories = categoriesData?.data?.categories || []; const rarities = raritiesData?.data?.rarities || []; const conditions = conditionsData?.data?.conditions || []; const priceRange = priceRangeData?.data || { min: 0, max: 1000 }; // Check if we have active filters const hasActiveFilters = useMemo(() => { return !!( filters.search || (filters.category && filters.category !== 'all') || (filters.rarity && filters.rarity !== 'all') || (filters.condition && filters.condition !== 'all') || (filters.deliveryMethod && filters.deliveryMethod !== 'all') || (filters.listType && filters.listType !== 'all') || (filters.dateListed && filters.dateListed !== 'all') || filters.minPrice || filters.maxPrice ); }, [filters]); // Pull to refresh handler const handlePullRefresh = useCallback(async () => { console.log('🔄 Pull to refresh - reloading marketplace pins'); await refetchPins(); }, [refetchPins]); // Pull to refresh hook const { containerRef: pullToRefreshContainerRef, isPulling, isRefreshing, pullDistance, progress, } = usePullToRefresh({ onRefresh: handlePullRefresh, threshold: 80, resistance: 0.5, disabled: isLoadingPins, }); // Handle search const handleSearch = (query: string) => { setDirectory('pins'); setSearchQuery(query); setFilters((prev) => ({ ...prev, search: query || undefined, offset: 0, })); setShowHomepage(false); // Hide homepage when searching updateUrl({ q: query || null, tab: activeTab, source, directory: null }); // Track marketplace unified search intent (best-effort) (async () => { try { const { analyticsService } = await import('@/services/analyticsService'); analyticsService.trackEvent({ event: 'search', category: 'marketplace', action: 'unified', label: query, metadata: { source }, }); } catch (_) {} })(); }; // Handle category selection const handleCategorySelect = (categoryId: string, subcategoryId?: string) => { if (categoryId === 'by-vendor') { setDirectory('vendors'); setSearchQuery(''); setFilters((prev) => ({ ...prev, search: undefined, category: undefined, subcategory: undefined, offset: 0, })); setSelectedCategory(undefined); setSelectedSubcategory(undefined); setShowHomepage(false); updateUrl({ q: null, category: null, subcategory: null, directory: 'vendors' }); return; } setDirectory('pins'); setSelectedCategory(categoryId); setSelectedSubcategory(subcategoryId); // Se uma subcategoria foi selecionada, usar seu nome como termo de busca if (subcategoryId) { // Encontrar a subcategoria para obter seu nome const allCategories = getAllCategories(); const parentCategory = allCategories.find((cat: any) => cat.id === categoryId); const subcategory = parentCategory?.subcategories?.find( (sub: any) => sub.id === subcategoryId ); if (subcategory) { // Usar o nome da subcategoria como termo de busca const searchTerm = subcategory.name; setSearchQuery(searchTerm); setFilters((prev) => ({ ...prev, search: searchTerm, category: categoryId, subcategory: subcategoryId, offset: 0, })); updateUrl({ q: searchTerm, category: categoryId, subcategory: subcategoryId || null, directory: null, }); } } else { // Se apenas uma categoria foi selecionada, manter comportamento atual const allCategories = getAllCategories(); const category = allCategories.find((cat: any) => cat.id === categoryId); if (category) { // Usar o nome da categoria como termo de busca const searchTerm = category.name; setSearchQuery(searchTerm); setFilters((prev) => ({ ...prev, search: searchTerm, category: categoryId, subcategory: undefined, offset: 0, })); updateUrl({ q: searchTerm, category: categoryId, subcategory: null, directory: null }); } } setShowHomepage(false); // Hide homepage when filtering by category }; // Handle filter changes const handleFiltersChange = (newFilters: Partial) => { setFilters((prev) => ({ ...prev, ...newFilters, offset: 0, })); updateUrl({ listType: newFilters.listType ?? filters.listType, condition: newFilters.condition ?? filters.condition, dateListed: newFilters.dateListed ?? filters.dateListed, sortBy: newFilters.sortBy ?? filters.sortBy, minPrice: newFilters.minPrice ?? filters.minPrice ?? null, maxPrice: newFilters.maxPrice ?? filters.maxPrice ?? null, }); }; // Clear all filters const clearFilters = () => { setDirectory('pins'); setFilters({ limit: ITEMS_LIMIT, offset: 0, sortBy: 'best_match', deliveryMethod: 'all', listType: 'all', condition: 'all', dateListed: 'all', }); setSearchQuery(''); setSelectedCategory(undefined); setSelectedSubcategory(undefined); setShowHomepage(true); // Show homepage when clearing filters showToast(tCommon('filtersCleared', { defaultValue: 'Filters cleared' }), 'success'); updateUrl({ q: null, category: null, subcategory: null, directory: null, listType: 'all', condition: 'all', dateListed: 'all', sortBy: 'best_match', minPrice: null, maxPrice: null, }); }; // Source and Tabs URL sync const handleSourceChange = (newSource: MarketplaceSource) => { setSource(newSource); updateUrl({ source: newSource }); }; const handleTabChange = (value: 'browse' | 'seller') => { setActiveTab(value); updateUrl({ tab: value }); }; const handleSellerTabChange = (value: SellerTab) => { setSellerTab(value); updateUrl({ sellerTab: value }); }; // Handle distance change const handleDistanceChange = (newDistance: string) => { setDistance(newDistance); updateUrl({ distance: newDistance }); }; // Handle sort by change const handleSortByChange = (newSortBy: string) => { setSortBy(newSortBy); updateUrl({ sortBy: newSortBy }); }; // Removed pagination logic - following my-pins pattern // Convert MarketplacePin to Pin format for PinDetailModal const convertMarketplacePinToPin = (marketplacePin: MarketplacePin): Pin => { return { id: marketplacePin.id, name: marketplacePin.name, image: marketplacePin.imageUrl, description: marketplacePin.description, origin: marketplacePin.origin, releaseYear: marketplacePin.releaseYear, rarity: marketplacePin.rarity === 'epic' ? 'ultra-rare' : marketplacePin.rarity, condition: marketplacePin.condition, availability: 'sale' as const, salePrice: marketplacePin.price, series: marketplacePin.series, likes: marketplacePin.likesCount, comments: marketplacePin.commentsCount, userId: marketplacePin.sellerId, owner: { id: marketplacePin.sellerId, name: marketplacePin.sellerName, avatar: marketplacePin.sellerAvatar, location: marketplacePin.sellerLocation, }, }; }; // Handle pin selection const handlePinClick = (pin: MarketplacePin) => { // Ao abrir o modal de detalhes, garanta que o modal de compra esteja fechado setShowPurchaseModal(false); setCheckoutPin(null); setSelectedPin(pin); }; // Handle pin click for components that expect Pin type const handlePinClickFromPin = (pin: Pin) => { // Convert Pin back to MarketplacePin if needed // For now, just use the pin as-is since SellerOverview might not need the exact conversion console.log('Pin clicked from SellerOverview:', pin); }; // Handle pin click from PinCard (receives pinId) // In marketplace, clicking a pin should open purchase modal, not details modal const handlePinCardClick = (pinId: string | undefined) => { if (!pinId) return; // Open purchase modal instead of details modal for marketplace pins handleBuyNow(pinId); }; // Handle purchase initiation const handleBuyPin = (pin: MarketplacePin) => { setCheckoutPin(pin); }; // Handle buy now from pin card const handleBuyNow = (pinId: string | undefined) => { if (!user) { const pin = pins.find((p: MarketplacePin) => p.id === pinId); showLoginModal({ action: 'buy', targetUser: pin ? { id: pin.sellerId, firstName: pin.sellerName || 'User', lastName: '', username: pin.sellerName, avatarUrl: pin.sellerAvatar, } : undefined, }); return; } const pin = pins.find((p: MarketplacePin) => p.id === pinId); if (pin) { // 🚫 Não permitir abrir o modal de compra para pins do próprio usuário if (user && pin.sellerId === user.id) { // Mostrar apenas o modal de detalhes setSelectedPin(pin); setShowPurchaseModal(false); showToast( tMkt('errors.cannotBuyOwnPin', { defaultValue: 'You cannot purchase your own pin' }), 'error' ); return; } setCheckoutPin(pin); setSelectedPin(pin); setShowPurchaseModal(true); } }; // Handle purchase success const handlePurchaseSuccess = () => { setCheckoutPin(null); setShowPurchaseModal(false); showToast( tPins('toasts.purchaseSuccess', { defaultValue: 'Purchase completed successfully!', }), 'success' ); // Refresh pins to reflect any changes refetchPins(); }; // Add handlers for like and save functionality const handlePinLike = async (pinId: string | undefined) => { if (!pinId) return; if (!user) { const pin = pins.find((p: MarketplacePin) => p.id === pinId); showLoginModal({ action: 'like', targetUser: pin ? { id: pin.sellerId, firstName: pin.sellerName || 'User', lastName: '', username: pin.sellerName, avatarUrl: pin.sellerAvatar, } : undefined, }); return; } try { // ✅ FIXED: Use new simplified toggle method const result = await pinsService.toggleLike(pinId); showToast(result.message, 'success'); // Refresh data to reflect the change refetchPins(); } catch (error: any) { console.error('Error toggling pin like:', error); showToast( error.message || tCommon('errors.toggleLikeFailed', { defaultValue: 'Failed to toggle like', }), 'error' ); } }; const handlePinSave = async (pinId: string | undefined) => { if (!pinId) return; if (!user) { const pin = pins.find((p: MarketplacePin) => p.id === pinId); showLoginModal({ action: 'save', targetUser: pin ? { id: pin.sellerId, firstName: pin.sellerName || 'User', lastName: '', username: pin.sellerName, avatarUrl: pin.sellerAvatar, } : undefined, }); return; } try { // ✅ FIXED: Use new simplified toggle method const result = await pinsService.toggleSave(pinId); showToast(result.message, 'success'); // Refresh data to reflect the change refetchPins(); } catch (error: any) { console.error('Error toggling pin save:', error); showToast( error.message || tCommon('errors.toggleSaveFailed', { defaultValue: 'Failed to toggle save', }), 'error' ); } }; const handlePinComment = (pinId: string | undefined) => { if (!pinId) return; // Open pin detail modal which has comment functionality const pin = pins.find((p: MarketplacePin) => p.id === pinId); if (pin) { setFocusOnComment(true); setSelectedPin(pin); } }; // Handle create pin const handleCreatePin = async (pinData: { title: string; description?: string; imageUrl: string; origin?: string; releaseYear?: number; originalPrice?: number; pinNumber?: string; availability?: 'display' | 'trade' | 'sale'; salePrice?: number; condition?: 'new' | 'like-new' | 'good' | 'fair'; boardId?: string; }) => { if (!user) { showToast( tAuth('toasts.loginRequiredToCreatePins', { defaultValue: 'Please sign in to create pins', }), 'error' ); return; } try { const createdPin = await pinsService.create({ title: pinData.title, description: pinData.description, imageUrl: pinData.imageUrl, origin: pinData.origin, releaseYear: pinData.releaseYear, originalPrice: pinData.originalPrice?.toString(), pinNumber: pinData.pinNumber, availability: pinData.availability || 'display', salePrice: pinData.salePrice, userId: user.id, }); // If a boardId is provided (from dropdown), add the pin to that board if (pinData.boardId && createdPin?.id) { try { await pinsService.addPinToBoard(createdPin.id, pinData.boardId); showToast( tCommon('success.pinCreatedAndAdded', { defaultValue: 'Pin created and added to board successfully!', }), 'success' ); } catch (boardError) { console.error('Error adding pin to board:', boardError); showToast( tCommon('warnings.pinCreatedButNotAddedToBoard', { defaultValue: 'Pin created successfully, but could not add to board', }), 'warning' ); } } else { showToast( tCommon('success.pinCreated', { defaultValue: 'Pin created successfully!', }), 'success' ); } setShowCreatePinModal(false); // Refresh pins data to potentially show the new pin if it's for sale refetchPins(); } catch (error: any) { console.error('Error creating pin:', error); showToast( error.message || tCommon('errors.createPinFailed', { defaultValue: 'Failed to create pin', }), 'error' ); } }; // Force sidebar collapse on mount and handle mobile detection useEffect(() => { console.log('🛍️ [MarketplacePage] Forcing sidebar to collapse for full-width layout'); setForceCollapsed(true); const handleResize = () => { setIsMobile(window.innerWidth < 1024); }; window.addEventListener('resize', handleResize); // Cleanup: restore sidebar state when leaving marketplace page return () => { console.log('🛍️ [MarketplacePage] Restoring sidebar state when leaving marketplace page'); setForceCollapsed(false); window.removeEventListener('resize', handleResize); }; }, [setForceCollapsed]); // Mobile view handling - exactly like messages (no page layout wrapper) if (isMobile) { return (
{/* Pull to refresh indicator */} {/* Mobile content - scroll handled by mobile-main-area */}
handleTabChange(value as 'browse' | 'seller')} className="w-full" > {/* Mobile View Switcher style (same pattern as Trading Map) */}
{user &&
} {user && ( )}
{directory === 'vendors' ? (
{/* Keep Categories access on mobile while in vendors directory */}
) : ( <> {/* Search between Buy/Sell and the toolbar (mobile) */}
{/* Mobile Toolbar with Categories, Distance and Sort By */}
{showHomepage ? ( ) : (
{isLoadingPins ? (
) : (
{pins.map((item: any) => { if (item.source === 'ebay') { return ( window.open(item.itemUrl, '_blank')} /> ); } else { return ( handleBuyNow(item.id)} onBuyNow={(pinId) => handleBuyNow(pinId)} onLike={() => handlePinLike(item)} onSave={() => handlePinSave(item)} onComment={() => handlePinComment(item)} showShoppingActions={true} /> ); } })} {pins.length === 0 && (

{tMkt('search.empty.title', { defaultValue: 'No pins found' })}

{searchQuery ? tMkt('search.empty.withQuery', { query: searchQuery, defaultValue: 'No results for "{{query}}". Try different search terms.', }) : tMkt('search.empty.suggestion', { defaultValue: 'Try adjusting your filters or search for specific pins.', })}

)}
)}
)} )}
{user && ( )}
); } // Desktop view - Layout inspirado no Instagram/Messages com sidebar fixa return ( <>
{/* Category Sidebar - Fixa como a lista de conversas */}
{/* Header da sidebar - STICKY */}
{/* Buy/Sell buttons styled like Trading Map tabs */}
{user && ( )}
{/* Navigation - SCROLLABLE */}
{activeTab === 'browse' && ( )} {activeTab === 'seller' && ( )}
{/* Main Content Area - Área principal livre */}
{activeTab === 'browse' ? ( <> {/* Conteúdo principal - SCROLLABLE */}
{directory === 'vendors' ? ( ) : ( <> {/* Desktop search always visible at top */}
{showHomepage ? ( ) : ( <> {/* Título do Marketplace */}

{tMkt('homepage.title', { defaultValue: 'Pin Marketplace' })}

{/* Search and filters - now inline with content */}
{isLoadingPins ? ( ) : ( <> {/* Special header for ISO pins */} {filters.listType === 'iso' && pins.length > 0 && (

{tMkt('iso.header', { defaultValue: 'Wanted Listings (ISO - In Search Of)', })}

{tMkt('iso.description', { defaultValue: 'These are pins that collectors are actively looking for. Contact the users if you have these items available.', })}

)}
{pins.map((item: any) => { if (item.source === 'ebay') { return ( window.open(item.itemUrl, '_blank') } /> ); } else { return ( handleBuyNow(item.id)} onBuyNow={(pinId) => handleBuyNow(pinId)} onLike={() => handlePinLike(item)} onSave={() => handlePinSave(item)} onComment={() => handlePinComment(item)} showShoppingActions={true} /> ); } })} {pins.length === 0 && (
{filters.listType === 'iso' ? ( ) : ( )}

{filters.listType === 'iso' ? tMkt('iso.empty.title', { defaultValue: 'No wanted listings found', }) : tMkt('search.empty.title', { defaultValue: 'No pins found', })}

{filters.listType === 'iso' ? searchQuery ? tMkt('iso.empty.withQuery', { query: searchQuery, defaultValue: 'No collectors are looking for "{{query}}". Try different search terms.', }) : tMkt('iso.empty.suggestion', { defaultValue: "No collectors have posted wanted listings yet. Be the first to post what you're looking for!", }) : searchQuery ? tMkt('search.empty.withQuery', { query: searchQuery, defaultValue: 'No results for "{{query}}". Try different search terms.', }) : tMkt('search.empty.suggestion', { defaultValue: 'Try adjusting your filters or search for specific pins.', })}

)}
)} )} )}
) : (
{sellerTab === 'overview' && ( )} {sellerTab === 'listings' && ( )} {sellerTab === 'orders' && ( )} {sellerTab === 'payouts' && ( )}
)}
{/* Modals */} {selectedPin && ( { setSelectedPin(null); setFocusOnComment(false); }} context="feed" onLike={handlePinLike} onSave={handlePinSave} focusOnComment={focusOnComment} /> )} {showPurchaseModal && selectedPin && ( setShowPurchaseModal(false)} onPurchaseSuccess={handlePurchaseSuccess} /> )} {isLoginModalOpen && } ); };