learning_ai_invt_trdg/mobile/components/dashboard/QuickPositions.tsx

160 lines
4.7 KiB
TypeScript

import React from 'react';
import { View, Text, Pressable, StyleSheet } from 'react-native';
import { useRouter } from 'expo-router';
import { LinearGradient } from 'expo-linear-gradient';
import { Colors, Fonts, FontSize, BorderRadius, Shadows, Spacing } from '@/constants/theme';
import { positions } from '@/constants/mockData';
import { formatPrice, formatPercent, formatCurrency } from '@/utils/format';
import Sparkline from '@/components/Sparkline';
import AnimatedCard from '@/components/AnimatedCard';
export default function QuickPositions() {
const router = useRouter();
const preview = positions.slice(0, 2);
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>ACTIVE POSITIONS</Text>
<Pressable onPress={() => router.push('/positions' as any)}>
<Text style={styles.seeAll}>See All </Text>
</Pressable>
</View>
{preview.map((pos, index) => {
const isPositive = pos.unrealizedPnl >= 0;
const color = isPositive ? Colors.accent.green : Colors.accent.red;
return (
<AnimatedCard key={pos.id} index={index + 5} style={[styles.cardWrapper, Shadows.card]}>
<LinearGradient
colors={['rgba(20,21,26,0.9)', 'rgba(14,15,18,0.95)']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.card}
>
<View style={[styles.accentLine, { backgroundColor: pos.side === 'BUY' ? Colors.accent.green : Colors.accent.red }]} />
<View style={styles.topRow}>
<Text style={styles.symbol}>{pos.symbol}</Text>
<View style={[styles.sideBadge, { backgroundColor: pos.side === 'BUY' ? 'rgba(0,255,136,0.15)' : 'rgba(255,51,102,0.15)' }]}>
<Text style={[styles.sideText, { color }]}>{pos.side === 'BUY' ? 'LONG' : 'SHORT'}</Text>
</View>
<Text style={styles.profileName}>{pos.profileName}</Text>
</View>
<View style={styles.priceRow}>
<View>
<Text style={styles.currentPrice}>{formatPrice(pos.currentPrice)}</Text>
<Text style={styles.entryPrice}>Entry {formatPrice(pos.entryPrice)}</Text>
</View>
<View style={styles.pnlContainer}>
<Text style={[styles.pnl, { color }]}>
{formatCurrency(pos.unrealizedPnl)} ({formatPercent(pos.unrealizedPnlPercent)})
</Text>
</View>
</View>
<Sparkline
data={pos.sparkData}
width={280}
height={32}
color={color}
/>
</LinearGradient>
</AnimatedCard>
);
})}
</View>
);
}
const styles = StyleSheet.create({
container: {
gap: 10,
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 4,
},
title: {
fontFamily: Fonts.inter.black,
fontSize: FontSize.micro,
color: Colors.text.secondary,
letterSpacing: 3,
},
seeAll: {
fontFamily: Fonts.inter.semiBold,
fontSize: FontSize.bodySmall,
color: Colors.accent.green,
},
cardWrapper: {
borderRadius: BorderRadius.large,
},
card: {
borderRadius: BorderRadius.large,
padding: Spacing.cardPadding,
borderWidth: 1,
borderColor: Colors.border.default,
overflow: 'hidden',
},
accentLine: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: 2,
opacity: 0.6,
},
topRow: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
marginBottom: 12,
},
symbol: {
fontFamily: Fonts.inter.black,
fontSize: FontSize.subheading,
color: Colors.text.primary,
letterSpacing: -0.3,
},
sideBadge: {
paddingHorizontal: 8,
paddingVertical: 3,
borderRadius: 6,
},
sideText: {
fontFamily: Fonts.inter.black,
fontSize: FontSize.micro,
letterSpacing: 1,
},
profileName: {
fontFamily: Fonts.inter.medium,
fontSize: FontSize.badge,
color: Colors.text.secondary,
marginLeft: 'auto',
},
priceRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'flex-end',
marginBottom: 12,
},
currentPrice: {
fontFamily: Fonts.mono.bold,
fontSize: FontSize.heading,
color: Colors.text.primary,
},
entryPrice: {
fontFamily: Fonts.mono.regular,
fontSize: FontSize.badge,
color: Colors.text.secondary,
marginTop: 2,
},
pnlContainer: {
alignItems: 'flex-end',
},
pnl: {
fontFamily: Fonts.mono.bold,
fontSize: FontSize.bodyLarge,
},
});