finsignal-segment-control
Version:
Segment control and news feed components for React web applications
230 lines (229 loc) • 12.2 kB
JavaScript
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));
}) })] }));
}