feat(ui): migrate settings and entries controls
This commit is contained in:
parent
324e34d537
commit
7f5f12509a
@ -5,6 +5,7 @@ import { Pencil, Trash2, Copy as CopyIcon, Search, Plus, Filter, LayoutGrid, Clo
|
|||||||
import type { BotState } from '../hooks/useWebSocket';
|
import type { BotState } from '../hooks/useWebSocket';
|
||||||
import { DEFAULT_BOT_STATE } from '../hooks/useWebSocket';
|
import { DEFAULT_BOT_STATE } from '../hooks/useWebSocket';
|
||||||
import { createManualEntry, deleteManualEntry, fetchManualEntries, type ManualEntryPayload } from '../lib/manualEntriesApi';
|
import { createManualEntry, deleteManualEntry, fetchManualEntries, type ManualEntryPayload } from '../lib/manualEntriesApi';
|
||||||
|
import { Button } from '../components/ui/Primitives';
|
||||||
|
|
||||||
interface Entry {
|
interface Entry {
|
||||||
stock_instance_id: string;
|
stock_instance_id: string;
|
||||||
@ -158,9 +159,9 @@ export const EntriesTab = ({ botState = DEFAULT_BOT_STATE }: EntriesTabProps) =>
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2 opacity-0 group-hover:opacity-100 transition-all translate-y-2 group-hover:translate-y-0">
|
<div className="flex gap-2 opacity-0 group-hover:opacity-100 transition-all translate-y-2 group-hover:translate-y-0">
|
||||||
<button onClick={() => setEditingEntry(entry)} className="p-3 bg-white/5 rounded-2xl text-gray-400 hover:text-white hover:bg-white/10 transition-all"><Pencil size={16} /></button>
|
<Button type="button" onClick={() => setEditingEntry(entry)} variant="ghost" className="p-3 bg-white/5 rounded-2xl text-gray-400 hover:text-white hover:bg-white/10 transition-all"><Pencil size={16} /></Button>
|
||||||
<button onClick={() => handleClone(entry)} className="p-3 bg-white/5 rounded-2xl text-gray-400 hover:text-green-400 hover:bg-green-500/10 transition-all"><CopyIcon size={16} /></button>
|
<Button type="button" onClick={() => handleClone(entry)} variant="ghost" className="p-3 bg-white/5 rounded-2xl text-gray-400 hover:text-green-400 hover:bg-green-500/10 transition-all"><CopyIcon size={16} /></Button>
|
||||||
<button onClick={() => handleDelete(entry.stock_instance_id)} className="p-3 bg-red-500/10 rounded-2xl text-red-500 hover:bg-red-500/20 transition-all"><Trash2 size={16} /></button>
|
<Button type="button" onClick={() => handleDelete(entry.stock_instance_id)} variant="ghost" className="p-3 bg-red-500/10 rounded-2xl text-red-500 hover:bg-red-500/20 transition-all"><Trash2 size={16} /></Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -233,14 +234,16 @@ export const EntriesTab = ({ botState = DEFAULT_BOT_STATE }: EntriesTabProps) =>
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<Button
|
||||||
|
type="button"
|
||||||
onClick={() => { setIsAdding(!isAdding); setEditingEntry(null); }}
|
onClick={() => { setIsAdding(!isAdding); setEditingEntry(null); }}
|
||||||
|
variant="ghost"
|
||||||
className={`flex items-center gap-3 px-8 py-4 rounded-[2rem] text-xs font-black uppercase tracking-widest transition-all ${isAdding ? 'bg-red-500/10 text-red-400 border border-red-500/20' : 'bg-white text-black shadow-2xl hover:scale-105'
|
className={`flex items-center gap-3 px-8 py-4 rounded-[2rem] text-xs font-black uppercase tracking-widest transition-all ${isAdding ? 'bg-red-500/10 text-red-400 border border-red-500/20' : 'bg-white text-black shadow-2xl hover:scale-105'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{isAdding ? <X size={18} /> : <Plus size={18} />}
|
{isAdding ? <X size={18} /> : <Plus size={18} />}
|
||||||
{isAdding ? 'Cancel Entry' : 'Manual Entry Injection'}
|
{isAdding ? 'Cancel Entry' : 'Manual Entry Injection'}
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 2. FORM DRAWER (IF ADDING/EDITING) */}
|
{/* 2. FORM DRAWER (IF ADDING/EDITING) */}
|
||||||
@ -251,7 +254,7 @@ export const EntriesTab = ({ botState = DEFAULT_BOT_STATE }: EntriesTabProps) =>
|
|||||||
<Plus size={20} className="text-green-400" />
|
<Plus size={20} className="text-green-400" />
|
||||||
{editingEntry ? `Update Identity: ${editingEntry.symbol}` : 'New Opportunity Initialization'}
|
{editingEntry ? `Update Identity: ${editingEntry.symbol}` : 'New Opportunity Initialization'}
|
||||||
</h3>
|
</h3>
|
||||||
<button onClick={() => { setIsAdding(false); setEditingEntry(null); }} className="text-gray-500 hover:text-white"><X size={20} /></button>
|
<Button type="button" onClick={() => { setIsAdding(false); setEditingEntry(null); }} variant="ghost" className="text-gray-500 hover:text-white"><X size={20} /></Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="max-w-4xl">
|
<div className="max-w-4xl">
|
||||||
<EntryForm
|
<EntryForm
|
||||||
@ -265,13 +268,15 @@ export const EntriesTab = ({ botState = DEFAULT_BOT_STATE }: EntriesTabProps) =>
|
|||||||
{/* 3. NAVIGATION CLUSTERS */}
|
{/* 3. NAVIGATION CLUSTERS */}
|
||||||
<div className="flex items-center p-1.5 bg-black/40 border border-white/5 rounded-[2rem] self-start shadow-2xl overflow-x-auto no-scrollbar max-w-full">
|
<div className="flex items-center p-1.5 bg-black/40 border border-white/5 rounded-[2rem] self-start shadow-2xl overflow-x-auto no-scrollbar max-w-full">
|
||||||
{navTabs.map((tab) => (
|
{navTabs.map((tab) => (
|
||||||
<button
|
<Button
|
||||||
|
type="button"
|
||||||
key={tab.id}
|
key={tab.id}
|
||||||
onClick={() => setActiveTab(tab.id)}
|
onClick={() => setActiveTab(tab.id)}
|
||||||
|
variant="ghost"
|
||||||
className={tabClass(activeTab === tab.id)}
|
className={tabClass(activeTab === tab.id)}
|
||||||
>
|
>
|
||||||
{tab.icon} {tab.label}
|
{tab.icon} {tab.label}
|
||||||
</button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{/* 4. CARDS GRID */}
|
{/* 4. CARDS GRID */}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
|
|||||||
import { useAuth } from '../components/AuthContext';
|
import { useAuth } from '../components/AuthContext';
|
||||||
import type { BotState } from '../hooks/useWebSocket';
|
import type { BotState } from '../hooks/useWebSocket';
|
||||||
import { updateCurrentUserProfile } from '../lib/profileApi';
|
import { updateCurrentUserProfile } from '../lib/profileApi';
|
||||||
|
import { Button, Input } from '../components/ui/Primitives';
|
||||||
|
|
||||||
interface SettingsTabProps {
|
interface SettingsTabProps {
|
||||||
botState: BotState;
|
botState: BotState;
|
||||||
@ -129,13 +130,13 @@ export const SettingsTab = ({ botState }: SettingsTabProps) => {
|
|||||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '20px' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '20px' }}>
|
||||||
<h3>User Configuration</h3>
|
<h3>User Configuration</h3>
|
||||||
{!editing ? (
|
{!editing ? (
|
||||||
<button onClick={() => setEditing(true)} className="primary-button">Edit Configuration</button>
|
<Button type="button" onClick={() => setEditing(true)} className="primary-button">Edit Configuration</Button>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ display: 'flex', gap: '10px' }}>
|
<div style={{ display: 'flex', gap: '10px' }}>
|
||||||
<button onClick={() => setEditing(false)} className="secondary-button" disabled={saving}>Cancel</button>
|
<Button type="button" onClick={() => setEditing(false)} className="secondary-button" disabled={saving}>Cancel</Button>
|
||||||
<button onClick={handleSave} className="primary-button" disabled={saving}>
|
<Button type="button" onClick={handleSave} className="primary-button" disabled={saving}>
|
||||||
{saving ? 'Saving...' : 'Save Changes'}
|
{saving ? 'Saving...' : 'Save Changes'}
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -143,7 +144,7 @@ export const SettingsTab = ({ botState }: SettingsTabProps) => {
|
|||||||
<div className="form-grid">
|
<div className="form-grid">
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>First Name</label>
|
<label>First Name</label>
|
||||||
<input
|
<Input
|
||||||
name="first_name"
|
name="first_name"
|
||||||
value={formData.first_name}
|
value={formData.first_name}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@ -152,7 +153,7 @@ export const SettingsTab = ({ botState }: SettingsTabProps) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>Last Name</label>
|
<label>Last Name</label>
|
||||||
<input
|
<Input
|
||||||
name="last_name"
|
name="last_name"
|
||||||
value={formData.last_name}
|
value={formData.last_name}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@ -162,7 +163,7 @@ export const SettingsTab = ({ botState }: SettingsTabProps) => {
|
|||||||
|
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>Your Role</label>
|
<label>Your Role</label>
|
||||||
<input
|
<Input
|
||||||
value={profile?.role || 'user'}
|
value={profile?.role || 'user'}
|
||||||
disabled={true}
|
disabled={true}
|
||||||
style={{ background: 'rgba(255, 255, 255, 0.02)', color: '#888', fontStyle: 'italic' }}
|
style={{ background: 'rgba(255, 255, 255, 0.02)', color: '#888', fontStyle: 'italic' }}
|
||||||
@ -172,7 +173,7 @@ export const SettingsTab = ({ botState }: SettingsTabProps) => {
|
|||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>Trading Enabled</label>
|
<label>Trading Enabled</label>
|
||||||
<div className="toggle-wrapper">
|
<div className="toggle-wrapper">
|
||||||
<input
|
<Input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="trade_enable"
|
name="trade_enable"
|
||||||
checked={formData.trade_enable}
|
checked={formData.trade_enable}
|
||||||
@ -188,7 +189,7 @@ export const SettingsTab = ({ botState }: SettingsTabProps) => {
|
|||||||
{/* Threshold Settings */}
|
{/* Threshold Settings */}
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>Buy Threshold (e.g. 0.02 for 2%)</label>
|
<label>Buy Threshold (e.g. 0.02 for 2%)</label>
|
||||||
<input
|
<Input
|
||||||
name="drop_threshold_for_buy"
|
name="drop_threshold_for_buy"
|
||||||
value={formData.drop_threshold_for_buy}
|
value={formData.drop_threshold_for_buy}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@ -198,7 +199,7 @@ export const SettingsTab = ({ botState }: SettingsTabProps) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>Sell Threshold (e.g. 0.01 for 1%)</label>
|
<label>Sell Threshold (e.g. 0.01 for 1%)</label>
|
||||||
<input
|
<Input
|
||||||
name="gain_threshold_for_sell"
|
name="gain_threshold_for_sell"
|
||||||
value={formData.gain_threshold_for_sell}
|
value={formData.gain_threshold_for_sell}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@ -209,7 +210,7 @@ export const SettingsTab = ({ botState }: SettingsTabProps) => {
|
|||||||
|
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>Market Poll Interval (ms)</label>
|
<label>Market Poll Interval (ms)</label>
|
||||||
<input
|
<Input
|
||||||
name="market_poll_interval_in_seconds"
|
name="market_poll_interval_in_seconds"
|
||||||
value={formData.market_poll_interval_in_seconds}
|
value={formData.market_poll_interval_in_seconds}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@ -225,7 +226,7 @@ export const SettingsTab = ({ botState }: SettingsTabProps) => {
|
|||||||
|
|
||||||
<div className="form-group full-width">
|
<div className="form-group full-width">
|
||||||
<label>Financial Modeling Prep API Key</label>
|
<label>Financial Modeling Prep API Key</label>
|
||||||
<input
|
<Input
|
||||||
name="FMP_API_KEY"
|
name="FMP_API_KEY"
|
||||||
value={formData.FMP_API_KEY}
|
value={formData.FMP_API_KEY}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@ -255,7 +256,7 @@ export const SettingsTab = ({ botState }: SettingsTabProps) => {
|
|||||||
|
|
||||||
<div className="form-group full-width">
|
<div className="form-group full-width">
|
||||||
<label>Paper ALPACA API Key</label>
|
<label>Paper ALPACA API Key</label>
|
||||||
<input
|
<Input
|
||||||
name="ALPACA_API_KEY"
|
name="ALPACA_API_KEY"
|
||||||
value={formData.ALPACA_API_KEY}
|
value={formData.ALPACA_API_KEY}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@ -265,7 +266,7 @@ export const SettingsTab = ({ botState }: SettingsTabProps) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group full-width">
|
<div className="form-group full-width">
|
||||||
<label>Paper ALPACA Secret Key</label>
|
<label>Paper ALPACA Secret Key</label>
|
||||||
<input
|
<Input
|
||||||
name="ALPACA_SECRET_KEY"
|
name="ALPACA_SECRET_KEY"
|
||||||
value={formData.ALPACA_SECRET_KEY}
|
value={formData.ALPACA_SECRET_KEY}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@ -276,7 +277,7 @@ export const SettingsTab = ({ botState }: SettingsTabProps) => {
|
|||||||
|
|
||||||
<div className="form-group full-width">
|
<div className="form-group full-width">
|
||||||
<label>Real ALPACA API Key</label>
|
<label>Real ALPACA API Key</label>
|
||||||
<input
|
<Input
|
||||||
name="REAL_ALPACA_API_KEY"
|
name="REAL_ALPACA_API_KEY"
|
||||||
value={formData.REAL_ALPACA_API_KEY}
|
value={formData.REAL_ALPACA_API_KEY}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@ -286,7 +287,7 @@ export const SettingsTab = ({ botState }: SettingsTabProps) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group full-width">
|
<div className="form-group full-width">
|
||||||
<label>Real ALPACA Secret Key</label>
|
<label>Real ALPACA Secret Key</label>
|
||||||
<input
|
<Input
|
||||||
name="REAL_ALPACA_SECRET_KEY"
|
name="REAL_ALPACA_SECRET_KEY"
|
||||||
value={formData.REAL_ALPACA_SECRET_KEY}
|
value={formData.REAL_ALPACA_SECRET_KEY}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user