UNPKG

finsignal-segment-control

Version:

Segment control and news feed components for React web applications

230 lines (229 loc) 12.2 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import * as React from 'react'; import { NewsSnippet } from './snippets/NewsSnippet'; import { SegmentControl } from './SegmentControl'; import './newsfeed.css'; // Моковые данные в формате FinSignal Feed const mockFeedItems = [ { content_id: '1', title: 'Tesla планирует открыть новый завод в Европе', body: 'Компания Tesla Motors объявила о планах строительства нового завода по производству электромобилей в Берлине. Ожидается создание более 10,000 рабочих мест.', type: 'news', source: 'provider', created_at: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(), score: 95, metadata: { news: { recommendation: { text: 'A breakout is expected at the resistance level of', priceRange: '$250-$260' }, stocks: [ { symbol: 'TSLA', price: '$242.50', change: '+5.2%', changeType: 'positive' } ] }, ai_categorization: { primaryCategory: 'Automotive', tags: ['Tesla', 'Electric Vehicles', 'Europe'] } } }, { content_id: '2', title: 'NVIDIA представила новую линейку графических процессоров', body: 'Новая серия RTX 5000 обещает на 40% больше производительности в задачах искусственного интеллекта и машинного обучения.', type: 'news', source: 'api', created_at: new Date(Date.now() - 4 * 60 * 60 * 1000).toISOString(), score: 92, metadata: { news: { stocks: [ { symbol: 'NVDA', price: '$495.80', change: '+12.3%', changeType: 'positive' } ] }, ai_categorization: { primaryCategory: 'Technology', tags: ['NVIDIA', 'AI', 'Hardware'] } } }, { content_id: '3', title: 'Microsoft расширяет сотрудничество с OpenAI', body: 'Microsoft объявила о дополнительных $10 млрд инвестиций в OpenAI для развития технологий искусственного интеллекта и интеграции GPT-4 в продукты.', type: 'news', source: 'provider', created_at: new Date(Date.now() - 6 * 60 * 60 * 1000).toISOString(), score: 90, metadata: { news: { recommendation: { text: 'A breakout is expected at the resistance level of', priceRange: '$385-$395' }, stocks: [ { symbol: 'MSFT', price: '$378.90', change: '+3.5%', changeType: 'positive' } ] }, ai_categorization: { primaryCategory: 'Technology', tags: ['Microsoft', 'OpenAI', 'AI'] } } }, { content_id: '4', title: 'Amazon планирует IPO своего подразделения AWS', body: 'По данным инсайдеров, Amazon рассматривает возможность выделения Amazon Web Services в отдельную публичную компанию.', type: 'news', source: 'api', created_at: new Date(Date.now() - 8 * 60 * 60 * 1000).toISOString(), score: 88, metadata: { news: { stocks: [ { symbol: 'AMZN', price: '$142.60', change: '-1.2%', changeType: 'negative' } ] }, ai_categorization: { primaryCategory: 'Cloud Computing', tags: ['Amazon', 'AWS', 'IPO'] } } }, { content_id: '5', title: 'Meta показала рекордный рост пользователей', body: 'Количество активных пользователей социальных сетей Meta выросло на 12% за квартал, достигнув нового исторического максимума.', type: 'news', source: 'provider', created_at: new Date(Date.now() - 12 * 60 * 60 * 1000).toISOString(), score: 87, metadata: { news: { stocks: [ { symbol: 'META', price: '$312.40', change: '+8.7%', changeType: 'positive' } ] }, ai_categorization: { primaryCategory: 'Social Media', tags: ['Meta', 'Facebook', 'Growth'] } } }, { content_id: '6', title: 'Apple выпустит новую линейку MacBook Pro', body: 'Apple готовится представить обновленные MacBook Pro с новыми процессорами M3 и улучшенными дисплеями ProMotion.', type: 'news', source: 'api', created_at: new Date(Date.now() - 18 * 60 * 60 * 1000).toISOString(), score: 85, metadata: { news: { recommendation: { text: 'A breakout is expected at the resistance level of', priceRange: '$185-$190' }, stocks: [ { symbol: 'AAPL', price: '$178.25', change: '+2.1%', changeType: 'positive' } ] }, ai_categorization: { primaryCategory: 'Technology', tags: ['Apple', 'MacBook', 'Hardware'] } } } ]; function formatTimeAgo(dateString) { const now = new Date(); const date = new Date(dateString); const diff = Math.floor((now.getTime() - date.getTime()) / 1000); if (diff < 60) return 'только что'; if (diff < 3600) return `${Math.floor(diff / 60)} мин назад`; if (diff < 86400) return `${Math.floor(diff / 3600)} ч назад`; return `${Math.floor(diff / 86400)} дн назад`; } export function FeedList({ feedType = 'trending', items = mockFeedItems, onItemClick, className = '', segments = [ { value: 'all', label: 'Все новости' }, { value: 'markets', label: 'Рынки' }, { value: 'crypto', label: 'Крипто' } ], showSegmentControl = true, onSegmentChange }) { var _a; const [selectedSegment, setSelectedSegment] = React.useState(((_a = segments[0]) === null || _a === void 0 ? void 0 : _a.value) || 'all'); const handleSegmentChange = (value) => { setSelectedSegment(value); onSegmentChange === null || onSegmentChange === void 0 ? void 0 : onSegmentChange(value); }; const getTypeLabel = (type) => { const labels = { signal: 'Сигнал', news: 'Новости', trigger: 'Триггер', market_vibe: 'Настроение', strategies: 'Стратегия', guides: 'Гайд', post: 'Пост' }; return labels[type] || type; }; const getTypeColor = (type) => { const colors = { signal: '#10b981', news: '#3b82f6', trigger: '#f59e0b', market_vibe: '#8b5cf6', strategies: '#ec4899', guides: '#06b6d4', post: '#6b7280' }; return colors[type] || colors.post; }; return (_jsxs(_Fragment, { children: [showSegmentControl && (_jsx("div", { style: { marginBottom: '16px' }, children: _jsx(SegmentControl, { options: segments, value: selectedSegment, onValueChange: handleSegmentChange }) })), _jsx("div", { className: `feed-list ${className}`, children: items.map((item) => { var _a, _b, _c, _d; // Рендерим news items через NewsSnippet if (item.type === 'news' && item.title && item.body) { return (_jsx(NewsSnippet, { id: item.content_id, title: item.title, content: item.body, recommendation: (_a = item.metadata.news) === null || _a === void 0 ? void 0 : _a.recommendation, stocks: (_b = item.metadata.news) === null || _b === void 0 ? void 0 : _b.stocks, onPress: () => onItemClick === null || onItemClick === void 0 ? void 0 : onItemClick(item), draggable: true, onDragStart: () => { console.log('Dragging news:', item.title); } }, item.content_id)); } // Для остальных типов оставляем обычный рендеринг return (_jsxs("div", { className: "feed-item", onClick: () => onItemClick === null || onItemClick === void 0 ? void 0 : onItemClick(item), children: [_jsxs("div", { className: "feed-item-header", children: [_jsx("span", { className: "feed-type-badge", style: { backgroundColor: getTypeColor(item.type) }, children: getTypeLabel(item.type) }), _jsx("span", { className: "feed-time", children: formatTimeAgo(item.created_at) })] }), item.title && _jsx("h3", { className: "feed-title", children: item.title }), item.body && _jsx("p", { className: "feed-body", children: item.body }), item.metadata.signal && (_jsxs("div", { className: "feed-signal-metrics", children: [_jsxs("div", { className: "signal-stock", children: [_jsx("span", { className: "signal-symbol", children: item.metadata.signal.symbol }), _jsx("span", { className: "signal-company", children: item.metadata.signal.companyName })] }), _jsxs("div", { className: "signal-prices", children: [_jsxs("div", { className: "signal-price-item", children: [_jsx("span", { className: "label", children: "Entry:" }), _jsx("span", { className: "value", children: item.metadata.signal.metrics.entry })] }), _jsxs("div", { className: "signal-price-item", children: [_jsx("span", { className: "label", children: "TP:" }), _jsx("span", { className: "value green", children: item.metadata.signal.metrics.takeProfit })] }), _jsxs("div", { className: "signal-price-item", children: [_jsx("span", { className: "label", children: "SL:" }), _jsx("span", { className: "value red", children: item.metadata.signal.metrics.stopLoss })] })] })] })), ((_c = item.metadata.news) === null || _c === void 0 ? void 0 : _c.stocks) && (_jsx("div", { className: "feed-stocks", children: item.metadata.news.stocks.map((stock, idx) => (_jsxs("div", { className: "stock-card", children: [_jsx("span", { className: "stock-symbol", children: stock.symbol }), _jsx("span", { className: "stock-price", children: stock.price }), _jsx("span", { className: `stock-change ${stock.changeType}`, children: stock.change })] }, idx))) })), ((_d = item.metadata.ai_categorization) === null || _d === void 0 ? void 0 : _d.tags) && (_jsx("div", { className: "feed-tags", children: item.metadata.ai_categorization.tags.map((tag, idx) => (_jsxs("span", { className: "feed-tag", children: ["#", tag] }, idx))) }))] }, item.content_id)); }) })] })); }