|
|
<script setup>
|
|
|
import {
|
|
|
X,
|
|
|
ChevronRight,
|
|
|
ChevronLeft,
|
|
|
Check,
|
|
|
Database,
|
|
|
Cpu,
|
|
|
Settings,
|
|
|
Eye,
|
|
|
BarChart,
|
|
|
Brain,
|
|
|
History,
|
|
|
TrendingUp,
|
|
|
Activity
|
|
|
} from 'lucide-vue-next';
|
|
|
import { ref, computed, watch } from 'vue';
|
|
|
|
|
|
const props = defineProps({
|
|
|
isOpen: Boolean,
|
|
|
universe: Array, // Symbols from AIBuilder
|
|
|
builderType: String, // 'classic' or 'neural'
|
|
|
initialType: String // 'Technical' or 'Fundamental'
|
|
|
});
|
|
|
|
|
|
const emit = defineEmits(['close', 'startTraining']);
|
|
|
|
|
|
const currentStep = ref(1);
|
|
|
const totalSteps = 6;
|
|
|
|
|
|
const steps = [
|
|
|
{ id: 1, name: 'Type', icon: Brain },
|
|
|
{ id: 2, name: 'Assets', icon: Database },
|
|
|
{ id: 3, name: 'Features', icon: BarChart },
|
|
|
{ id: 4, name: 'Algorithm', icon: Cpu },
|
|
|
{ id: 5, name: 'Config', icon: Settings },
|
|
|
{ id: 6, name: 'Review', icon: Eye },
|
|
|
];
|
|
|
|
|
|
// Form State
|
|
|
const form = ref({
|
|
|
type: 'Technical',
|
|
|
assetClass: 'Stock',
|
|
|
selectedSymbol: null, // Changed from array to single value
|
|
|
selectedFeatures: [],
|
|
|
selectedModels: [],
|
|
|
config: {
|
|
|
period: '2y',
|
|
|
testSize: 0.2
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// Watch for initialType changes and update form
|
|
|
watch(() => props.initialType, (newType) => {
|
|
|
if (newType && props.isOpen) {
|
|
|
form.value.type = newType;
|
|
|
}
|
|
|
}, { immediate: true });
|
|
|
|
|
|
// Reset form type when modal opens
|
|
|
watch(() => props.isOpen, (isOpen) => {
|
|
|
if (isOpen && props.initialType) {
|
|
|
form.value.type = props.initialType;
|
|
|
}
|
|
|
});
|
|
|
|
|
|
const assetClasses = ['Stock', 'Index', 'Future', 'Crypto', 'ETF'];
|
|
|
|
|
|
// Technical Indicators
|
|
|
const technicalFeatures = [
|
|
|
// Momentum
|
|
|
{ id: 'rsi', name: 'RSI (14)', category: 'Momentum' },
|
|
|
{ id: 'stoch_k', name: 'Stoch %K', category: 'Momentum' },
|
|
|
{ id: 'stoch_d', name: 'Stoch %D', category: 'Momentum' },
|
|
|
{ id: 'williams', name: 'Williams %R', category: 'Momentum' },
|
|
|
{ id: 'cci', name: 'CCI', category: 'Momentum' },
|
|
|
// Trend
|
|
|
{ id: 'sma_20', name: 'SMA 20', category: 'Trend' },
|
|
|
{ id: 'sma_50', name: 'SMA 50', category: 'Trend' },
|
|
|
{ id: 'sma_200', name: 'SMA 200', category: 'Trend' },
|
|
|
{ id: 'ema_9', name: 'EMA 9', category: 'Trend' },
|
|
|
{ id: 'ema_21', name: 'EMA 21', category: 'Trend' },
|
|
|
{ id: 'macd', name: 'MACD Base', category: 'Trend' },
|
|
|
{ id: 'macd_signal', name: 'MACD Signal', category: 'Trend' },
|
|
|
{ id: 'macd_hist', name: 'MACD Hist', category: 'Trend' },
|
|
|
{ id: 'ichimoku_a', name: 'Ichimoku A', category: 'Trend' },
|
|
|
{ id: 'ichimoku_b', name: 'Ichimoku B', category: 'Trend' },
|
|
|
// Volatility
|
|
|
{ id: 'bb_high', name: 'BB High', category: 'Volatility' },
|
|
|
{ id: 'bb_low', name: 'BB Low', category: 'Volatility' },
|
|
|
{ id: 'atr', name: 'Average True Range', category: 'Volatility' },
|
|
|
{ id: 'keltner_h', name: 'Keltner High', category: 'Volatility' },
|
|
|
{ id: 'donchian_h', name: 'Donchian High', category: 'Volatility' },
|
|
|
// Volume
|
|
|
{ id: 'volume', name: 'Raw Volume', category: 'Volume' },
|
|
|
{ id: 'obv', name: 'On-Balance Vol', category: 'Volume' },
|
|
|
{ id: 'vwap', name: 'VWAP', category: 'Volume' },
|
|
|
{ id: 'cmf', name: 'Chaikin MF', category: 'Volume' },
|
|
|
];
|
|
|
|
|
|
// Fundamental Indicators
|
|
|
const fundamentalFeatures = [
|
|
|
{ id: 'pe', name: 'P/E Ratio', category: 'Valuation' },
|
|
|
{ id: 'pb', name: 'P/B Ratio', category: 'Valuation' },
|
|
|
{ id: 'rev_growth', name: 'Revenue Growth', category: 'Performance' },
|
|
|
{ id: 'eps', name: 'EPS', category: 'Performance' },
|
|
|
{ id: 'debt_equity', name: 'Debt/Equity', category: 'Solvency' },
|
|
|
{ id: 'roe', name: 'ROE', category: 'Profitability' },
|
|
|
{ id: 'curr_ratio', name: 'Current Ratio', category: 'Liquidity' },
|
|
|
{ id: 'div_yield', name: 'Dividend Yield', category: 'Valuation' },
|
|
|
];
|
|
|
|
|
|
const filteredFeatures = computed(() => {
|
|
|
return form.value.type === 'Technical' ? technicalFeatures : fundamentalFeatures;
|
|
|
});
|
|
|
|
|
|
// Classic ML Models
|
|
|
const classicModels = [
|
|
|
{ id: 'rf', name: 'Random Forest', description: 'Ensemble of decision trees' },
|
|
|
{ id: 'xgb', name: 'XGBoost', description: 'Gradient boosting framework' },
|
|
|
{ id: 'lr', name: 'Linear Regression', description: 'Baseline linear model' },
|
|
|
{ id: 'svm', name: 'SVM', description: 'Support Vector Machines' },
|
|
|
];
|
|
|
|
|
|
// Neural DL Models
|
|
|
const neuralModels = [
|
|
|
{ id: 'transformer', name: 'Transformer', description: 'Attention-based sequence model' },
|
|
|
{ id: 'lstm', name: 'LSTM RNN', description: 'Long Short-Term Memory' },
|
|
|
{ id: 'cnn', name: 'CNN 1D', description: 'Convolutional neural network' },
|
|
|
{ id: 'mlp', name: 'Neural Network (MLP)', description: 'Multi-layer Perceptron' },
|
|
|
];
|
|
|
|
|
|
const filteredModels = computed(() => {
|
|
|
return props.builderType === 'classic' ? classicModels : neuralModels;
|
|
|
});
|
|
|
|
|
|
const nextStep = () => {
|
|
|
if (currentStep.value < totalSteps) currentStep.value++;
|
|
|
};
|
|
|
|
|
|
const prevStep = () => {
|
|
|
if (currentStep.value > 1) currentStep.value
|
|
|
};
|
|
|
|
|
|
const toggleSelection = (list, item) => {
|
|
|
const idx = list.indexOf(item);
|
|
|
if (idx > -1) list.splice(idx, 1);
|
|
|
else list.push(item);
|
|
|
};
|
|
|
|
|
|
const handleStart = () => {
|
|
|
emit('startTraining', { ...form.value, builderType: props.builderType });
|
|
|
emit('close');
|
|
|
reset();
|
|
|
};
|
|
|
|
|
|
const reset = () => {
|
|
|
currentStep.value = 1;
|
|
|
form.value.selectedSymbol = null; // Reset to null
|
|
|
form.value.selectedFeatures = [];
|
|
|
form.value.selectedModels = [];
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
<div v-if="isOpen" class="fixed inset-0 z-[110] flex items-center justify-center md:p-4">
|
|
|
<div class="absolute inset-0 bg-slate-950/95 backdrop-blur-md" @click="$emit('close')"></div>
|
|
|
|
|
|
<div class="relative w-full h-full md:h-auto md:max-w-4xl bg-[#0b1120] border-0 md:border md:border-slate-800 md:rounded-3xl shadow-2xl overflow-hidden flex flex-col md:max-h-[90vh]">
|
|
|
<!
|
|
|
<div class="flex items-center justify-between px-4 md:px-8 py-4 md:py-6 border-b border-slate-800 bg-slate-900/40">
|
|
|
<div class="flex items-center gap-3 md:gap-4">
|
|
|
<div class="hidden sm:flex w-10 h-10 bg-blue-600/10 rounded-xl items-center justify-center border border-blue-500/20 text-blue-400">
|
|
|
<component :is="builderType === 'classic' ? Cpu : Brain" class="w-5 h-5" />
|
|
|
</div>
|
|
|
<div>
|
|
|
<h2 class="text-base md:text-xl font-bold text-white tracking-tight">Create {{ builderType === 'classic' ? 'ML' : 'DL' }} Model</h2>
|
|
|
<div class="flex items-center gap-2 mt-0.5">
|
|
|
<span class="px-2 py-0.5 bg-indigo-500/10 text-indigo-400 text-[9px] md:text-[10px] font-black rounded-lg border border-indigo-500/20 uppercase tracking-widest">
|
|
|
{{ form.type }} Engine
|
|
|
</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<button @click="$emit('close')" class="p-2 hover:bg-slate-800 rounded-xl text-slate-500 hover:text-white transition-all">
|
|
|
<X class="w-5 h-5 md:w-6 md:h-6" />
|
|
|
</button>
|
|
|
</div>
|
|
|
|
|
|
<!
|
|
|
<div class="bg-slate-950 px-4 md:px-8 py-4 border-b border-slate-800 flex justify-between gap-1 overflow-x-auto custom-scrollbar">
|
|
|
<div v-for="s in steps" :key="s.id" class="flex items-center gap-2 md:gap-3 shrink-0 group">
|
|
|
<div
|
|
|
class="flex items-center justify-center w-8 h-8 md:w-10 md:h-10 rounded-xl transition-all duration-300 border shadow-inner"
|
|
|
:class="[
|
|
|
currentStep === s.id ? 'bg-blue-600 border-blue-500 text-white shadow-lg shadow-blue-600/20 scale-105' :
|
|
|
currentStep > s.id ? 'bg-emerald-500/10 border-emerald-500/30 text-emerald-400' : 'bg-slate-900 border-slate-800 text-slate-600'
|
|
|
]"
|
|
|
>
|
|
|
<Check v-if="currentStep > s.id" class="w-4 h-4 md:w-5 md:h-5" />
|
|
|
<component v-else :is="s.icon" class="w-4 h-4 md:w-5 md:h-5" />
|
|
|
</div>
|
|
|
<span v-if="currentStep === s.id" class="text-[10px] md:text-xs font-black uppercase tracking-widest text-white hidden xs:block">
|
|
|
{{ s.name }}
|
|
|
</span>
|
|
|
<div v-if="s.id < totalSteps" class="w-4 md:w-8 h-px bg-slate-800 mx-1 md:mx-2"></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!
|
|
|
<div class="flex-1 overflow-y-auto p-4 md:p-10 custom-scrollbar">
|
|
|
|
|
|
<!
|
|
|
<div v-if="currentStep === 1" class="space-y-6 md:space-y-8 animate-in slide-in-from-bottom duration-300">
|
|
|
<div class="space-y-1 md:space-y-2">
|
|
|
<h3 class="text-xl md:text-3xl font-black text-white tracking-tight">Select Logic Engine</h3>
|
|
|
<p class="text-xs md:text-base text-slate-400 max-w-lg">Choose between historical price patterns or fundamental health metrics.</p>
|
|
|
</div>
|
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 md:gap-6">
|
|
|
<div
|
|
|
@click="form.type = 'Technical'"
|
|
|
class="p-6 md:p-8 rounded-2xl md:rounded-3xl border-2 transition-all cursor-pointer group"
|
|
|
:class="form.type === 'Technical' ? 'bg-blue-600/10 border-blue-500' : 'bg-slate-900 border-slate-800 hover:border-slate-700'"
|
|
|
>
|
|
|
<div class="w-12 h-12 md:w-16 md:h-16 bg-blue-500/10 rounded-2xl flex items-center justify-center mb-6 group-hover:scale-110 transition-transform">
|
|
|
<TrendingUp class="w-6 h-6 md:w-8 md:h-8" :class="form.type === 'Technical' ? 'text-blue-400' : 'text-slate-600'" />
|
|
|
</div>
|
|
|
<h4 class="font-bold text-lg md:text-xl text-white mb-2">Technical Analysis</h4>
|
|
|
<p class="text-xs md:text-sm text-slate-500 leading-relaxed">Predict next-day price movements using RSI, MACD, and Volume signals.</p>
|
|
|
</div>
|
|
|
<div
|
|
|
@click="form.type = 'Fundamental'"
|
|
|
class="p-6 md:p-8 rounded-2xl md:rounded-3xl border-2 transition-all cursor-pointer group"
|
|
|
:class="form.type === 'Fundamental' ? 'bg-emerald-600/10 border-emerald-500' : 'bg-slate-900 border-slate-800 hover:border-slate-700'"
|
|
|
>
|
|
|
<div class="w-12 h-12 md:w-16 md:h-16 bg-emerald-500/10 rounded-2xl flex items-center justify-center mb-6 group-hover:scale-110 transition-transform">
|
|
|
<Activity class="w-6 h-6 md:w-8 md:h-8" :class="form.type === 'Fundamental' ? 'text-emerald-400' : 'text-slate-600'" />
|
|
|
</div>
|
|
|
<h4 class="font-bold text-lg md:text-xl text-white mb-2">Fundamental Health</h4>
|
|
|
<p class="text-xs md:text-sm text-slate-500 leading-relaxed">Assess long-term growth by training on P/E, EPS, and Revenue trends.</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!
|
|
|
<div v-if="currentStep === 2" class="space-y-6 md:space-y-10 animate-in slide-in-from-bottom duration-300">
|
|
|
<div class="space-y-2">
|
|
|
<h3 class="text-xl md:text-3xl font-black text-white tracking-tight">Target Selection</h3>
|
|
|
<p class="text-xs md:text-base text-slate-400">Choose the specific instrument for the training pipeline.</p>
|
|
|
</div>
|
|
|
|
|
|
<div class="space-y-4">
|
|
|
<label class="text-[10px] md:text-xs font-black text-slate-600 uppercase tracking-widest block">Sector / Asset Class</label>
|
|
|
<div class="flex flex-wrap gap-2 md:gap-3">
|
|
|
<button
|
|
|
v-for="ac in assetClasses" :key="ac"
|
|
|
@click="form.assetClass = ac"
|
|
|
class="px-4 py-2 md:px-6 md:py-2.5 rounded-xl text-[10px] md:text-xs font-bold transition-all border shadow-sm"
|
|
|
:class="form.assetClass === ac ? 'bg-white text-slate-950 border-white font-black' : 'bg-slate-900/50 text-slate-400 border-slate-800 hover:border-slate-700'"
|
|
|
>
|
|
|
{{ ac }}
|
|
|
</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="space-y-4">
|
|
|
<label class="text-[10px] md:text-xs font-black text-slate-600 uppercase tracking-widest block">Available Universe</label>
|
|
|
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3">
|
|
|
<div
|
|
|
v-for="s in universe" :key="s"
|
|
|
@click="form.selectedSymbol = s"
|
|
|
class="p-4 rounded-2xl border-2 cursor-pointer transition-all flex flex-col gap-2 group relative overflow-hidden"
|
|
|
:class="form.selectedSymbol === s ? 'bg-blue-600 border-blue-500 shadow-xl shadow-blue-600/20' : 'bg-slate-900/50 border-slate-800 hover:border-slate-700'"
|
|
|
>
|
|
|
<span class="text-sm md:text-lg font-black tracking-tighter" :class="form.selectedSymbol === s ? 'text-white' : 'text-slate-300'">{{ s }}</span>
|
|
|
<span class="text-[8px] md:text-[10px] font-bold uppercase" :class="form.selectedSymbol === s ? 'text-blue-200' : 'text-slate-600'">Equity</span>
|
|
|
<div v-if="form.selectedSymbol === s" class="absolute top-2 right-2 w-4 h-4 bg-white rounded-full flex items-center justify-center">
|
|
|
<Check class="w-3 h-3 text-blue-600" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div v-if="universe.length === 0" class="p-6 bg-amber-500/5 border border-amber-500/20 rounded-2xl text-center">
|
|
|
<Info class="w-6 h-6 text-amber-500 mx-auto mb-2" />
|
|
|
<p class="text-xs text-amber-500 italic max-w-xs mx-auto">No symbols found in the primary vault. Please add assets via the Data Hub first.</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!
|
|
|
<div v-if="currentStep === 3" class="space-y-6 md:space-y-8 animate-in slide-in-from-bottom duration-300">
|
|
|
<div class="space-y-2">
|
|
|
<h3 class="text-xl md:text-3xl font-black text-white tracking-tight">Input Parameters</h3>
|
|
|
<p class="text-xs md:text-base text-slate-400">Select the features to feed into the neural architecture.</p>
|
|
|
</div>
|
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 md:gap-4">
|
|
|
<div
|
|
|
v-for="f in filteredFeatures" :key="f.id"
|
|
|
@click="toggleSelection(form.selectedFeatures, f.id)"
|
|
|
class="flex items-center justify-between p-4 md:p-6 bg-slate-900 border border-slate-800 rounded-2xl md:rounded-3xl hover:bg-slate-800/50 transition-all cursor-pointer group"
|
|
|
:class="form.selectedFeatures.includes(f.id) ? 'border-blue-500/50 bg-blue-500/5' : ''"
|
|
|
>
|
|
|
<div class="flex flex-col">
|
|
|
<span class="text-sm md:text-base font-bold text-white group-hover:text-blue-400 transition-colors">{{ f.name }}</span>
|
|
|
<span class="text-[9px] md:text-[10px] text-slate-500 uppercase font-black tracking-widest mt-0.5">{{ f.category }}</span>
|
|
|
</div>
|
|
|
<div class="w-6 h-6 md:w-7 md:h-7 rounded-lg flex items-center justify-center border-2 transition-all" :class="form.selectedFeatures.includes(f.id) ? 'bg-blue-600 border-blue-500 shadow-lg shadow-blue-500/20' : 'bg-slate-950 border-slate-800 group-hover:border-slate-700'">
|
|
|
<Check v-if="form.selectedFeatures.includes(f.id)" class="w-3.5 h-3.5 md:w-4 md:h-4 text-white" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!
|
|
|
<div v-if="currentStep === 4" class="space-y-6 md:space-y-8 animate-in slide-in-from-bottom duration-300">
|
|
|
<div class="space-y-2">
|
|
|
<h3 class="text-xl md:text-3xl font-black text-white">Algorithms</h3>
|
|
|
<p class="text-sm text-slate-500">Choose the optimal processing model for your data type.</p>
|
|
|
</div>
|
|
|
<div class="space-y-3 md:space-y-4">
|
|
|
<div
|
|
|
v-for="m in filteredModels" :key="m.id"
|
|
|
@click="toggleSelection(form.selectedModels, m.id)"
|
|
|
class="p-4 md:p-6 rounded-2xl md:rounded-3xl border-2 transition-all cursor-pointer flex items-center gap-4 md:gap-6"
|
|
|
:class="form.selectedModels.includes(m.id) ? 'bg-indigo-600/10 border-indigo-500 shadow-xl shadow-indigo-600/10' : 'bg-slate-900 border-slate-800 hover:border-slate-700'"
|
|
|
>
|
|
|
<div class="w-12 h-12 md:w-16 md:h-16 bg-slate-800 rounded-2xl flex items-center justify-center shrink-0 border border-slate-700">
|
|
|
<Cpu class="w-6 h-6 md:w-8 md:h-8" :class="form.selectedModels.includes(m.id) ? 'text-indigo-400' : 'text-slate-600'" />
|
|
|
</div>
|
|
|
<div class="flex-1">
|
|
|
<h4 class="font-bold text-base md:text-lg text-white mb-0.5">{{ m.name }}</h4>
|
|
|
<p class="text-[10px] md:text-sm text-slate-500 leading-snug">{{ m.description }}</p>
|
|
|
</div>
|
|
|
<div class="w-6 h-6 md:w-7 md:h-7 rounded-full border-2 flex items-center justify-center transition-all" :class="form.selectedModels.includes(m.id) ? 'bg-indigo-500 border-indigo-500 shadow-lg' : 'bg-slate-950 border-slate-800'">
|
|
|
<Check v-if="form.selectedModels.includes(m.id)" class="w-3.5 h-3.5 text-white" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!
|
|
|
<div v-if="currentStep === 5" class="space-y-8 md:space-y-12 animate-in slide-in-from-bottom duration-300">
|
|
|
<div class="space-y-2">
|
|
|
<h3 class="text-xl md:text-3xl font-black text-white">Hyperparameters</h3>
|
|
|
<p class="text-sm text-slate-500">Fine-tune the training pipeline and temporal constraints.</p>
|
|
|
</div>
|
|
|
<div class="grid grid-cols-1 gap-8 md:gap-12">
|
|
|
<div class="space-y-4">
|
|
|
<label class="text-[10px] md:text-xs font-black text-slate-500 uppercase tracking-widest flex items-center gap-2">
|
|
|
<History class="w-4 h-4 text-blue-400" /> Historical Period
|
|
|
</label>
|
|
|
<div class="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
|
|
<button v-for="p in ['6m', '1y', '2y', '5y']" :key="p" @click="form.config.period = p" :class="form.config.period === p ? 'bg-blue-600 border-blue-500 text-white shadow-lg font-black' : 'bg-slate-900 border-slate-800 text-slate-500 hover:text-white'" class="py-3 md:py-4 rounded-xl border-2 text-xs md:text-sm transition-all">{{ p.toUpperCase() }}</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="space-y-6">
|
|
|
<div class="flex justify-between items-center">
|
|
|
<label class="text-[10px] md:text-xs font-black text-slate-500 uppercase tracking-widest flex items-center gap-2">
|
|
|
<TrendingUp class="w-4 h-4 text-emerald-400" /> Train/Test Partitioning
|
|
|
</label>
|
|
|
<span class="text-xs font-bold text-blue-400 bg-blue-500/10 px-3 py-1 rounded-lg border border-blue-500/20">{{ Math.round((1 - form.config.testSize) * 100) }}% Train / {{ Math.round(form.config.testSize * 100) }}% Test</span>
|
|
|
</div>
|
|
|
<input type="range" v-model.number="form.config.testSize" min="0.1" max="0.5" step="0.05" class="w-full h-2 bg-slate-800 rounded-lg appearance-none cursor-pointer accent-blue-500" />
|
|
|
<div class="flex justify-between text-[10px] font-bold text-slate-600">
|
|
|
<span>90/10 Split</span>
|
|
|
<span>50/50 Split</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!
|
|
|
<div v-if="currentStep === 6" class="space-y-6 md:space-y-10 animate-in slide-in-from-bottom duration-300">
|
|
|
<div class="text-center space-y-3">
|
|
|
<div class="w-20 h-20 bg-blue-600/10 rounded-[2.5rem] flex items-center justify-center mx-auto border border-blue-500/20 shadow-2xl relative">
|
|
|
<Eye class="w-10 h-10 text-blue-400" />
|
|
|
<div class="absolute -bottom-1 -right-1 w-8 h-8 bg-emerald-500 rounded-full flex items-center justify-center border-4 border-[#0b1120]">
|
|
|
<Check class="w-4 h-4 text-white" />
|
|
|
</div>
|
|
|
</div>
|
|
|
<div>
|
|
|
<h3 class="text-2xl md:text-3xl font-black text-white">Consolidate Task</h3>
|
|
|
<p class="text-xs md:text-sm text-slate-500">Ready to initiate the training sequence?</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
|
<div class="bg-slate-900/50 p-5 md:p-6 rounded-2xl border border-slate-800 flex items-center gap-4">
|
|
|
<div class="w-10 h-10 bg-blue-500/10 rounded-xl flex items-center justify-center text-blue-400 border border-blue-500/20">
|
|
|
<Target class="w-5 h-5" />
|
|
|
</div>
|
|
|
<div>
|
|
|
<div class="text-[9px] font-black text-slate-500 uppercase tracking-[.2em] mb-1">Target Asset</div>
|
|
|
<div class="text-base font-bold text-white">{{ form.selectedSymbol || 'None' }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="bg-slate-900/50 p-5 md:p-6 rounded-2xl border border-slate-800 flex items-center gap-4">
|
|
|
<div class="w-10 h-10 bg-indigo-500/10 rounded-xl flex items-center justify-center text-indigo-400 border border-indigo-500/20">
|
|
|
<Cpu class="w-5 h-5" />
|
|
|
</div>
|
|
|
<div>
|
|
|
<div class="text-[9px] font-black text-slate-500 uppercase tracking-[.2em] mb-1">Architectures</div>
|
|
|
<div class="text-base font-bold text-white">{{ form.selectedModels.length }} Selected</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="bg-slate-900/50 p-5 md:p-6 rounded-2xl border border-slate-800 col-span-1 sm:col-span-2 space-y-2">
|
|
|
<div class="text-[9px] font-black text-slate-500 uppercase tracking-[.2em] flex items-center gap-2">
|
|
|
<BarChart class="w-3 h-3 text-emerald-400" /> Logic Vector Scope
|
|
|
</div>
|
|
|
<div class="flex flex-wrap gap-2 pt-1">
|
|
|
<span v-for="featId in form.selectedFeatures" :key="featId" class="px-2 py-1 bg-slate-950 border border-slate-700/50 rounded-lg text-[10px] font-bold text-slate-300">{{ featId.toUpperCase() }}</span>
|
|
|
<span v-if="form.selectedFeatures.length === 0" class="text-xs italic text-slate-600">No features mapped</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<!
|
|
|
<div class="px-4 md:px-10 py-4 md:py-8 border-t border-slate-800 bg-slate-900/40 flex flex-col sm:flex-row gap-4 items-center justify-between">
|
|
|
<div class="flex items-center gap-2 md:gap-4 order-2 sm:order-1 w-full sm:w-auto">
|
|
|
<button
|
|
|
@click="prevStep"
|
|
|
:disabled="currentStep === 1"
|
|
|
class="flex-1 sm:flex-none flex items-center justify-center gap-2 px-6 py-3 bg-slate-800 hover:bg-slate-700 disabled:opacity-30 text-white font-bold rounded-2xl transition-all border border-slate-700"
|
|
|
>
|
|
|
<ChevronLeft class="w-4 h-4" /> <span class="hidden md:inline">Back</span>
|
|
|
</button>
|
|
|
|
|
|
<div class="hidden md:flex flex-col">
|
|
|
<span class="text-[10px] font-black text-slate-500 uppercase tracking-widest">Pipeline Phase</span>
|
|
|
<span class="text-sm font-bold text-white">{{ currentStep }} / {{ totalSteps }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="w-full sm:w-auto order-1 sm:order-2">
|
|
|
<button
|
|
|
v-if="currentStep < totalSteps"
|
|
|
@click="nextStep"
|
|
|
class="w-full sm:w-auto flex items-center justify-center gap-2 px-10 py-3 md:py-4 bg-blue-600 hover:bg-blue-500 text-white font-black rounded-2xl transition-all shadow-xl shadow-blue-600/20 active:scale-95"
|
|
|
>
|
|
|
Carry On <ChevronRight class="w-5 h-5" />
|
|
|
</button>
|
|
|
<button
|
|
|
v-else
|
|
|
@click="handleStart"
|
|
|
class="w-full sm:w-auto flex items-center justify-center gap-2 px-12 py-3 md:py-4 bg-indigo-600 hover:bg-indigo-500 text-white font-black rounded-2xl transition-all shadow-xl shadow-indigo-600/20 active:scale-95"
|
|
|
>
|
|
|
Initiate Training <Zap class="w-5 h-5" />
|
|
|
</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<style scoped>
|
|
|
.xs\:block { display: none; }
|
|
|
@media (min-width: 480px) { .xs\:block { display: block; } }
|
|
|
|
|
|
.custom-scrollbar::-webkit-scrollbar { width: 5px; height: 5px; }
|
|
|
.custom-scrollbar::-webkit-scrollbar-track { background: transparent; }
|
|
|
.custom-scrollbar::-webkit-scrollbar-thumb { background: #1e293b; border-radius: 10px; }
|
|
|
|
|
|
.animate-in {
|
|
|
animation-duration: 0.4s;
|
|
|
animation-fill-mode: both;
|
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
|
}
|
|
|
|
|
|
@keyframes slideInBottom {
|
|
|
from { opacity: 0; transform: translateY(30px); }
|
|
|
to { opacity: 1; transform: translateY(0); }
|
|
|
}
|
|
|
.slide-in-from-bottom { animation-name: slideInBottom; }
|
|
|
|
|
|
input[type="range"]::-webkit-slider-thumb {
|
|
|
-webkit-appearance: none;
|
|
|
appearance: none;
|
|
|
width: 20px;
|
|
|
height: 20px;
|
|
|
background: white;
|
|
|
border-radius: 50%;
|
|
|
cursor: pointer;
|
|
|
border: 4px solid #3b82f6;
|
|
|
box-shadow: 0 0 10px rgba(59, 130, 246, 0.5);
|
|
|
}
|
|
|
</style>
|
|
|
|