feat: реализация модуля "Биржа" с переносом функционала из Маркета

- Создан новый раздел "Биржа" во всех кабинетах с иконкой TrendingUp
- Перенесены вкладки "Инвестиции" и "Бизнес" из /market в /exchange
- Обновлена навигация сайдбара: кнопка "Биржа" между "Экономика" и "Настройки"
- Маркет теперь содержит только "Товары" и "Заявки" (2 вкладки вместо 4)
- Сохранена полная функциональность без потери данных
- Безопасная реализация с резервными копиями оригинальных компонентов

Структура Exchange модуля:
- src/components/exchange/exchange-dashboard.tsx
- src/components/exchange/tabs/investments-tab.tsx
- src/components/exchange/tabs/business-tab.tsx
- src/components/exchange/types/exchange.types.ts
- src/app/exchange/page.tsx

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Veronika Smirnova
2025-08-22 11:46:49 +03:00
parent f9dde25515
commit e7e4889102
9 changed files with 595 additions and 31 deletions

10
src/app/exchange/page.tsx Normal file
View File

@ -0,0 +1,10 @@
'use client'
import { ExchangeDashboard } from '@/components/exchange'
import { useAuth } from '@/hooks/useAuth'
export default function ExchangePage() {
const { user } = useAuth()
return <ExchangeDashboard userRole={user?.organization?.type} />
}

View File

@ -12,6 +12,7 @@ import {
MessageCircle,
Settings,
Store,
TrendingUp,
Truck,
Users,
Warehouse,
@ -28,7 +29,7 @@ import { useSidebar } from '@/hooks/useSidebar'
// Компонент для отображения логистических заявок (только для логистики)
function LogisticsOrdersNotification() {
const { data: pendingData, refetch: refetchPending } = useQuery(GET_PENDING_SUPPLIES_COUNT, {
const { data: pendingData, refetch: _refetchPending } = useQuery(GET_PENDING_SUPPLIES_COUNT, {
fetchPolicy: 'cache-first',
errorPolicy: 'ignore',
})
@ -46,7 +47,7 @@ function LogisticsOrdersNotification() {
// Компонент для отображения поставок фулфилмента (только поставки, не заявки на партнерство)
function FulfillmentSuppliesNotification() {
const { data: pendingData, refetch: refetchPending } = useQuery(GET_PENDING_SUPPLIES_COUNT, {
const { data: pendingData, refetch: _refetchPending } = useQuery(GET_PENDING_SUPPLIES_COUNT, {
fetchPolicy: 'cache-first',
errorPolicy: 'ignore',
})
@ -64,7 +65,7 @@ function FulfillmentSuppliesNotification() {
// Компонент для отображения входящих заказов поставщика (только входящие заказы, не заявки на партнерство)
function WholesaleOrdersNotification() {
const { data: pendingData, refetch: refetchPending } = useQuery(GET_PENDING_SUPPLIES_COUNT, {
const { data: pendingData, refetch: _refetchPending } = useQuery(GET_PENDING_SUPPLIES_COUNT, {
fetchPolicy: 'cache-first',
errorPolicy: 'ignore',
})
@ -185,6 +186,10 @@ export function Sidebar({ isRootInstance = false }: { isRootInstance?: boolean }
router.push('/settings')
}
const handleExchangeClick = () => {
router.push('/exchange')
}
const handleMarketClick = () => {
router.push('/market')
}
@ -256,6 +261,7 @@ export function Sidebar({ isRootInstance = false }: { isRootInstance?: boolean }
const isHomeActive = pathname === '/home'
const isEconomicsActive = pathname === '/economics'
const isSettingsActive = pathname === '/settings'
const isExchangeActive = pathname.startsWith('/exchange')
const isMarketActive = pathname.startsWith('/market')
const isMessengerActive = pathname.startsWith('/messenger')
const isServicesActive = pathname.startsWith('/services')
@ -679,6 +685,22 @@ export function Sidebar({ isRootInstance = false }: { isRootInstance?: boolean }
{!isCollapsed && <span className="ml-3">Экономика</span>}
</Button>
<Button
variant={isExchangeActive ? 'secondary' : 'ghost'}
className={`w-full ${
isCollapsed ? 'justify-center px-2 h-9' : 'justify-start h-10'
} text-left transition-all duration-200 text-xs ${
isExchangeActive
? 'bg-white/20 text-white hover:bg-white/30'
: 'text-white/80 hover:bg-white/10 hover:text-white'
} cursor-pointer`}
onClick={handleExchangeClick}
title={isCollapsed ? 'Биржа' : ''}
>
<TrendingUp className={`${isCollapsed ? 'h-4 w-4' : 'h-4 w-4'} flex-shrink-0`} />
{!isCollapsed && <span className="ml-3">Биржа</span>}
</Button>
<Button
variant={isSettingsActive ? 'secondary' : 'ghost'}
className={`w-full ${

View File

@ -0,0 +1,66 @@
'use client'
import { Sidebar } from '@/components/dashboard/sidebar'
import { Card } from '@/components/ui/card'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { useSidebar } from '@/hooks/useSidebar'
import { ExchangeBusinessTab } from './tabs/business-tab'
import { ExchangeInvestmentsTab } from './tabs/investments-tab'
import type { ExchangeDashboardProps } from './types/exchange.types'
export function ExchangeDashboard({ userRole = 'SELLER' }: ExchangeDashboardProps) {
const { getSidebarMargin } = useSidebar()
return (
<div className="h-screen flex overflow-hidden">
<Sidebar />
<main className={`flex-1 ${getSidebarMargin()} px-6 py-4 overflow-hidden transition-all duration-300`}>
<div className="h-full w-full flex flex-col">
{/* Заголовок раздела */}
<div className="flex items-center space-x-3 mb-6 flex-shrink-0">
<div className="w-12 h-12 bg-gradient-to-r from-purple-500 to-blue-500 rounded-xl flex items-center justify-center">
<span className="text-white font-bold text-xl"></span>
</div>
<div>
<h1 className="text-2xl font-bold text-white">Биржа</h1>
<p className="text-white/60">Инвестиции и бизнес-возможности</p>
</div>
</div>
{/* Основной контент с табами */}
<div className="flex-1 overflow-hidden">
<Tabs defaultValue="investments" className="h-full flex flex-col">
<TabsList className="grid w-full grid-cols-2 bg-white/5 backdrop-blur border-white/10 flex-shrink-0">
<TabsTrigger
value="investments"
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70"
>
Инвестиции
</TabsTrigger>
<TabsTrigger
value="business"
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70"
>
Бизнес
</TabsTrigger>
</TabsList>
<TabsContent value="investments" className="flex-1 overflow-hidden mt-6">
<Card className="glass-card h-full overflow-hidden p-6">
<ExchangeInvestmentsTab userRole={userRole} />
</Card>
</TabsContent>
<TabsContent value="business" className="flex-1 overflow-hidden mt-6">
<Card className="glass-card h-full overflow-hidden p-6">
<ExchangeBusinessTab userRole={userRole} />
</Card>
</TabsContent>
</Tabs>
</div>
</div>
</main>
</div>
)
}

View File

@ -0,0 +1,9 @@
// Главный экспорт модуля Exchange
export { ExchangeDashboard } from './exchange-dashboard'
// Экспорт компонентов вкладок
export { ExchangeInvestmentsTab } from './tabs/investments-tab'
export { ExchangeBusinessTab } from './tabs/business-tab'
// Экспорт типов
export type { ExchangeDashboardProps, ExchangeTabProps, ExchangeTabType, ExchangeConfig } from './types/exchange.types'

View File

@ -0,0 +1,59 @@
'use client'
import { Building, Users, Target, Briefcase } from 'lucide-react'
import { Card } from '@/components/ui/card'
import type { ExchangeTabProps } from '../types/exchange.types'
export function ExchangeBusinessTab(_props: ExchangeTabProps) {
return (
<div className="h-full flex flex-col space-y-4 overflow-hidden">
{/* Заголовок с иконкой */}
<div className="flex items-center space-x-3 flex-shrink-0 mb-4">
<Briefcase className="h-6 w-6 text-orange-400" />
<div>
<h3 className="text-lg font-semibold text-white">Бизнес</h3>
<p className="text-white/60 text-sm">Бизнес-возможности и развитие</p>
</div>
</div>
{/* Контент раздела */}
<div className="flex-1 overflow-auto space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<Card className="bg-white/5 backdrop-blur border-white/10 p-6">
<div className="flex items-center space-x-3 mb-4">
<Building className="h-8 w-8 text-orange-400" />
<h4 className="text-lg font-semibold text-white">Франшизы</h4>
</div>
<p className="text-white/60 text-sm">Готовые бизнес-решения и франшизы в сфере логистики и торговли</p>
</Card>
<Card className="bg-white/5 backdrop-blur border-white/10 p-6">
<div className="flex items-center space-x-3 mb-4">
<Users className="h-8 w-8 text-blue-400" />
<h4 className="text-lg font-semibold text-white">Партнёрство</h4>
</div>
<p className="text-white/60 text-sm">Поиск бизнес-партнёров для совместных проектов и развития</p>
</Card>
<Card className="bg-white/5 backdrop-blur border-white/10 p-6">
<div className="flex items-center space-x-3 mb-4">
<Target className="h-8 w-8 text-green-400" />
<h4 className="text-lg font-semibold text-white">Консалтинг</h4>
</div>
<p className="text-white/60 text-sm">Бизнес-консультации и стратегическое планирование развития</p>
</Card>
</div>
<div className="text-center py-8">
<div className="w-16 h-16 bg-white/10 rounded-full flex items-center justify-center mx-auto mb-4">
<Briefcase className="h-8 w-8 text-white/40" />
</div>
<p className="text-white/60 text-lg mb-2">Раздел в разработке</p>
<p className="text-white/40 text-sm">Бизнес-функционал будет доступен в ближайших обновлениях</p>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,63 @@
'use client'
import { TrendingUp, DollarSign, BarChart3 } from 'lucide-react'
import { Card } from '@/components/ui/card'
import type { ExchangeTabProps } from '../types/exchange.types'
export function ExchangeInvestmentsTab(_props: ExchangeTabProps) {
return (
<div className="h-full flex flex-col space-y-4 overflow-hidden">
{/* Заголовок с иконкой */}
<div className="flex items-center space-x-3 flex-shrink-0 mb-4">
<TrendingUp className="h-6 w-6 text-green-400" />
<div>
<h3 className="text-lg font-semibold text-white">Инвестиции</h3>
<p className="text-white/60 text-sm">Инвестиционные возможности и проекты</p>
</div>
</div>
{/* Контент раздела */}
<div className="flex-1 overflow-auto space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<Card className="bg-white/5 backdrop-blur border-white/10 p-6">
<div className="flex items-center space-x-3 mb-4">
<DollarSign className="h-8 w-8 text-green-400" />
<h4 className="text-lg font-semibold text-white">Инвестиционные проекты</h4>
</div>
<p className="text-white/60 text-sm">
Поиск и анализ перспективных инвестиционных проектов в сфере логистики и e-commerce
</p>
</Card>
<Card className="bg-white/5 backdrop-blur border-white/10 p-6">
<div className="flex items-center space-x-3 mb-4">
<BarChart3 className="h-8 w-8 text-blue-400" />
<h4 className="text-lg font-semibold text-white">Аналитика рынка</h4>
</div>
<p className="text-white/60 text-sm">
Исследования и аналитические отчёты для принятия инвестиционных решений
</p>
</Card>
<Card className="bg-white/5 backdrop-blur border-white/10 p-6">
<div className="flex items-center space-x-3 mb-4">
<TrendingUp className="h-8 w-8 text-purple-400" />
<h4 className="text-lg font-semibold text-white">Доходность</h4>
</div>
<p className="text-white/60 text-sm">Отслеживание доходности инвестиций и планирование бюджета</p>
</Card>
</div>
<div className="text-center py-8">
<div className="w-16 h-16 bg-white/10 rounded-full flex items-center justify-center mx-auto mb-4">
<TrendingUp className="h-8 w-8 text-white/40" />
</div>
<p className="text-white/60 text-lg mb-2">Раздел в разработке</p>
<p className="text-white/40 text-sm">Функционал инвестиций будет доступен в ближайших обновлениях</p>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,19 @@
// Типы для модуля Биржи
export interface ExchangeDashboardProps {
userRole?: 'SELLER' | 'WHOLESALE' | 'FULFILLMENT' | 'LOGIST'
}
export interface ExchangeTabProps {
userRole?: 'SELLER' | 'WHOLESALE' | 'FULFILLMENT' | 'LOGIST'
}
export type ExchangeTabType = 'investments' | 'business'
export interface ExchangeConfig {
defaultTab: ExchangeTabType
availableTabs: {
id: ExchangeTabType
name: string
enabled: boolean
}[]
}

View File

@ -9,9 +9,7 @@ import { useSidebar } from '@/hooks/useSidebar'
import { FavoritesDashboard } from '../favorites/favorites-dashboard'
import { MarketBusiness } from './market-business'
import { MarketCategories } from './market-categories'
import { MarketInvestments } from './market-investments'
import { MarketProducts } from './market-products'
import { MarketRequests } from './market-requests'
@ -48,7 +46,7 @@ export function MarketDashboard() {
{/* Основной контент с табами */}
<div className="flex-1 overflow-hidden">
<Tabs
defaultValue="investments"
defaultValue="products"
className="h-full flex flex-col"
onValueChange={(value) => {
if (value === 'products') {
@ -58,19 +56,7 @@ export function MarketDashboard() {
}
}}
>
<TabsList className="grid w-full grid-cols-4 bg-white/5 backdrop-blur border-white/10 flex-shrink-0">
<TabsTrigger
value="investments"
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70"
>
Инвестиции
</TabsTrigger>
<TabsTrigger
value="business"
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70"
>
Бизнес
</TabsTrigger>
<TabsList className="grid w-full grid-cols-2 bg-white/5 backdrop-blur border-white/10 flex-shrink-0">
<TabsTrigger
value="products"
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70"
@ -85,18 +71,6 @@ export function MarketDashboard() {
</TabsTrigger>
</TabsList>
<TabsContent value="investments" className="flex-1 overflow-hidden mt-6">
<Card className="glass-card h-full overflow-hidden p-6">
<MarketInvestments />
</Card>
</TabsContent>
<TabsContent value="business" className="flex-1 overflow-hidden mt-6">
<Card className="glass-card h-full overflow-hidden p-6">
<MarketBusiness />
</Card>
</TabsContent>
<TabsContent value="requests" className="flex-1 overflow-hidden mt-6">
<Card className="glass-card h-full overflow-hidden p-6">
<MarketRequests />