learning_ai_invt_trdg/mobile/app/(tabs)/strategies.tsx

298 lines
8.2 KiB
TypeScript

import React, { useState } from 'react';
import { View, Text, ScrollView, Switch, StyleSheet } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { useRouter } from 'expo-router';
import { LinearGradient } from 'expo-linear-gradient';
import { Colors, Fonts, FontSize, BorderRadius, Shadows, Spacing } from '@/constants/theme';
import { strategies } from '@/constants/mockData';
import { formatCurrency } from '@/utils/format';
import AnimatedCard from '@/components/AnimatedCard';
import PillBadge from '@/components/PillBadge';
import PressableScale from '@/components/PressableScale';
const RISK_COLORS: Record<string, { color: string; label: string; icon: string }> = {
aggressive: { color: Colors.accent.orange, label: 'Aggressive', icon: '\u{1F525}' },
balanced: { color: Colors.accent.green, label: 'Balanced', icon: '\u{2696}\u{FE0F}' },
safe: { color: Colors.accent.blue, label: 'Conservative', icon: '\u{1F6E1}\u{FE0F}' },
};
function StrategyCard({ strategy, index }: { strategy: typeof strategies[0]; index: number }) {
const [isActive, setIsActive] = useState(strategy.isActive);
const risk = RISK_COLORS[strategy.riskStyle];
const isPositive = strategy.netPnl >= 0;
return (
<AnimatedCard index={index} style={[Shadows.card, { borderRadius: BorderRadius.large }]}>
<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: risk.color }]} />
<View style={styles.cardHeader}>
<Text style={styles.stratName}>{strategy.name}</Text>
<Switch
value={isActive}
onValueChange={setIsActive}
trackColor={{ false: Colors.background.elevated, true: 'rgba(0,255,136,0.3)' }}
thumbColor={isActive ? Colors.accent.green : '#666'}
/>
</View>
<PillBadge
label={`${risk.icon} ${risk.label}`}
color={risk.color}
bgColor={`${risk.color}20`}
/>
<View style={styles.statsGrid}>
<StatCell label="TRADES" value={strategy.tradeCount.toString()} />
<StatCell label="WINS" value={strategy.wins.toString()} />
<StatCell label="WIN RATE" value={`${strategy.winRate}%`} color={Colors.accent.green} />
<StatCell label="NET P&L" value={formatCurrency(strategy.netPnl)} color={isPositive ? Colors.accent.green : Colors.accent.red} mono />
</View>
<View style={styles.assetPills}>
{strategy.symbols.map(s => (
<View key={s} style={styles.assetPill}>
<Text style={styles.assetText}>{s}</Text>
</View>
))}
</View>
<View style={styles.capitalRow}>
<Text style={styles.capitalLabel}>
${strategy.allocatedCapital.toLocaleString()} allocated
</Text>
</View>
<View style={styles.progressSection}>
<View style={styles.progressHeader}>
<Text style={styles.progressLabel}>Daily Target</Text>
<Text style={styles.progressValue}>
${strategy.dailyProgress} / ${strategy.dailyTarget}
</Text>
</View>
<View style={styles.progressTrack}>
<LinearGradient
colors={['#00ff88', '#00cc6a']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
style={[styles.progressFill, { width: `${(strategy.dailyProgress / strategy.dailyTarget) * 100}%` as any }]}
/>
</View>
</View>
</LinearGradient>
</AnimatedCard>
);
}
function StatCell({ label, value, color, mono }: { label: string; value: string; color?: string; mono?: boolean }) {
return (
<View style={styles.statCell}>
<Text style={styles.statLabel}>{label}</Text>
<Text style={[
styles.statValue,
color ? { color } : null,
mono ? { fontFamily: Fonts.mono.extraBold } : null,
]}>
{value}
</Text>
</View>
);
}
export default function StrategiesScreen() {
const insets = useSafeAreaInsets();
const router = useRouter();
return (
<View style={[styles.container, { paddingTop: insets.top }]}>
<View style={styles.headerSection}>
<Text style={styles.sectionLabel}>STRATEGIES</Text>
<Text style={styles.pageTitle}>My Strategies</Text>
</View>
<ScrollView
style={styles.scroll}
contentContainerStyle={styles.content}
showsVerticalScrollIndicator={false}
>
{strategies.map((s, i) => (
<StrategyCard key={s.id} strategy={s} index={i} />
))}
<PressableScale
haptic="medium"
onPress={() => router.push('/marketplace')}
style={styles.ctaWrapper}
>
<LinearGradient
colors={['#00ff88', '#00cc6a']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.ctaButton}
>
<Text style={styles.ctaText}>EXPLORE MARKETPLACE</Text>
</LinearGradient>
</PressableScale>
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.background.primary,
},
headerSection: {
padding: Spacing.screenPadding,
paddingBottom: 0,
},
sectionLabel: {
fontFamily: Fonts.inter.black,
fontSize: FontSize.micro,
color: Colors.accent.green,
letterSpacing: 4,
marginBottom: 8,
},
pageTitle: {
fontFamily: Fonts.inter.black,
fontSize: FontSize.hero,
color: Colors.text.primary,
letterSpacing: -0.5,
marginBottom: 16,
},
scroll: {
flex: 1,
},
content: {
padding: Spacing.screenPadding,
gap: 16,
paddingBottom: 120,
},
card: {
borderRadius: BorderRadius.large,
padding: Spacing.cardPaddingLarge,
borderWidth: 1,
borderColor: Colors.border.default,
overflow: 'hidden',
gap: 12,
},
accentLine: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: 2,
opacity: 0.6,
},
cardHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
stratName: {
fontFamily: Fonts.inter.black,
fontSize: 18,
color: Colors.text.primary,
},
statsGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 10,
marginTop: 4,
},
statCell: {
width: '46%',
},
statLabel: {
fontFamily: Fonts.inter.black,
fontSize: FontSize.micro,
color: Colors.text.secondary,
letterSpacing: 0.8,
marginBottom: 3,
},
statValue: {
fontFamily: Fonts.inter.extraBold,
fontSize: FontSize.bodyLarge,
color: Colors.text.primary,
},
assetPills: {
flexDirection: 'row',
gap: 6,
flexWrap: 'wrap',
},
assetPill: {
backgroundColor: 'rgba(255,255,255,0.05)',
paddingHorizontal: 10,
paddingVertical: 4,
borderRadius: BorderRadius.xs,
},
assetText: {
fontFamily: Fonts.mono.medium,
fontSize: FontSize.micro,
color: Colors.text.secondary,
},
capitalRow: {
marginTop: 2,
},
capitalLabel: {
fontFamily: Fonts.mono.medium,
fontSize: FontSize.bodySmall,
color: Colors.text.secondary,
},
progressSection: {
gap: 6,
},
progressHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
progressLabel: {
fontFamily: Fonts.inter.semiBold,
fontSize: FontSize.bodySmall,
color: Colors.text.secondary,
},
progressValue: {
fontFamily: Fonts.mono.bold,
fontSize: FontSize.bodySmall,
color: Colors.text.primary,
},
progressTrack: {
height: 4,
borderRadius: 2,
backgroundColor: Colors.background.elevated,
overflow: 'hidden',
},
progressFill: {
height: 4,
borderRadius: 2,
},
ctaWrapper: {
marginTop: 8,
borderRadius: 18,
shadowColor: 'rgba(0,255,136,0.4)',
shadowOffset: { width: 0, height: 12 },
shadowOpacity: 1,
shadowRadius: 36,
elevation: 8,
},
ctaButton: {
height: 56,
borderRadius: 18,
alignItems: 'center',
justifyContent: 'center',
},
ctaText: {
fontFamily: Fonts.inter.black,
fontSize: FontSize.bodySmall,
color: '#000',
letterSpacing: 2,
},
});