refactor(web): normalize entry form theme surface
This commit is contained in:
parent
df00e977d4
commit
75a3ac4ecf
@ -99,7 +99,7 @@ Current public bundle:
|
|||||||
- [x] Normalize `web/src/components/StrategyWizard.tsx`
|
- [x] Normalize `web/src/components/StrategyWizard.tsx`
|
||||||
- [x] Normalize `web/src/tabs/ReconciliationAuditPanel.tsx`
|
- [x] Normalize `web/src/tabs/ReconciliationAuditPanel.tsx`
|
||||||
- [x] Normalize `web/src/components/GlobalConfigManager.tsx`
|
- [x] Normalize `web/src/components/GlobalConfigManager.tsx`
|
||||||
- [ ] Normalize `web/src/components/EntryForm.tsx`
|
- [x] Normalize `web/src/components/EntryForm.tsx`
|
||||||
- [ ] Normalize remaining old admin/config surfaces
|
- [ ] Normalize remaining old admin/config surfaces
|
||||||
- [ ] Remove remaining dark-only bespoke surface systems
|
- [ ] Remove remaining dark-only bespoke surface systems
|
||||||
- [ ] Replace remaining one-off controls with shared primitives where practical
|
- [ ] Replace remaining one-off controls with shared primitives where practical
|
||||||
|
|||||||
@ -5,12 +5,18 @@ import { tradingRuntime } from '../lib/runtime';
|
|||||||
import { createManualEntry, updateManualEntry } from '../lib/manualEntriesApi';
|
import { createManualEntry, updateManualEntry } from '../lib/manualEntriesApi';
|
||||||
import { getPlatformAccessToken } from '../lib/authSession';
|
import { getPlatformAccessToken } from '../lib/authSession';
|
||||||
import { createRequestId } from '../../../shared/request-id.js';
|
import { createRequestId } from '../../../shared/request-id.js';
|
||||||
|
import { Button } from './ui/button';
|
||||||
|
import { Input } from './ui/input';
|
||||||
|
|
||||||
interface EntryFormProps {
|
interface EntryFormProps {
|
||||||
onSuccess: () => void;
|
onSuccess: () => void;
|
||||||
initialData?: any;
|
initialData?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tableInputClass = 'h-9 rounded border-[var(--border)] bg-[var(--input)] px-2 py-1 text-sm';
|
||||||
|
const numericInputClass = `${tableInputClass} font-mono text-right`;
|
||||||
|
const checkboxLabelClass = 'flex cursor-pointer items-center text-[10px] text-[var(--muted-foreground)] hover:text-[var(--foreground)]';
|
||||||
|
|
||||||
export function EntryForm({ onSuccess, initialData }: EntryFormProps) {
|
export function EntryForm({ onSuccess, initialData }: EntryFormProps) {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
@ -106,7 +112,7 @@ export function EntryForm({ onSuccess, initialData }: EntryFormProps) {
|
|||||||
// Remove temp fields from DB payload
|
// Remove temp fields from DB payload
|
||||||
delete (payload as any).execute_order;
|
delete (payload as any).execute_order;
|
||||||
|
|
||||||
// --- 🚀 REAL TRADE EXECUTION ---
|
// --- REAL TRADE EXECUTION ---
|
||||||
if (formData.execute_order && !initialData) {
|
if (formData.execute_order && !initialData) {
|
||||||
if (!Number.isFinite(payload.quantity) || !payload.symbol.trim()) {
|
if (!Number.isFinite(payload.quantity) || !payload.symbol.trim()) {
|
||||||
alert('Symbol and a valid quantity are required to execute a trade.');
|
alert('Symbol and a valid quantity are required to execute a trade.');
|
||||||
@ -125,7 +131,7 @@ export function EntryForm({ onSuccess, initialData }: EntryFormProps) {
|
|||||||
tp: payload.gain_threshold_for_sell
|
tp: payload.gain_threshold_for_sell
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmTrade = window.confirm(`🚀 EXECUTE LIVE TRADE (via Bot)?\n(Note: Uses currently configured Bot account)\n\nSymbol: ${payload.symbol}\nSide: BUY\nQty: ${payload.quantity}`);
|
const confirmTrade = window.confirm(`EXECUTE LIVE TRADE (via Bot)?\n(Note: Uses currently configured Bot account)\n\nSymbol: ${payload.symbol}\nSide: BUY\nQty: ${payload.quantity}`);
|
||||||
if (!confirmTrade) return;
|
if (!confirmTrade) return;
|
||||||
|
|
||||||
const apiUrl = tradingRuntime.tradingApiUrl;
|
const apiUrl = tradingRuntime.tradingApiUrl;
|
||||||
@ -145,7 +151,7 @@ export function EntryForm({ onSuccess, initialData }: EntryFormProps) {
|
|||||||
throw new Error(`Execution Failed: ${resData.error}`);
|
throw new Error(`Execution Failed: ${resData.error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
alert(`✅ Trade Executed! Order ID: ${resData.orderId}`);
|
alert(`Trade Executed! Order ID: ${resData.orderId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initialData?.stock_instance_id) {
|
if (initialData?.stock_instance_id) {
|
||||||
@ -185,6 +191,8 @@ export function EntryForm({ onSuccess, initialData }: EntryFormProps) {
|
|||||||
<th style={{ width: '10%' }} className="text-right">Qty</th>
|
<th style={{ width: '10%' }} className="text-right">Qty</th>
|
||||||
<th style={{ width: '10%' }} className="text-right">Stop Loss</th>
|
<th style={{ width: '10%' }} className="text-right">Stop Loss</th>
|
||||||
<th style={{ width: '10%' }} className="text-right">Take Profit</th>
|
<th style={{ width: '10%' }} className="text-right">Take Profit</th>
|
||||||
|
<th style={{ width: '12%' }}>Buy Time</th>
|
||||||
|
<th style={{ width: '10%' }} className="text-right">Exit Price</th>
|
||||||
<th style={{ width: '11%' }}>Actions</th>
|
<th style={{ width: '11%' }}>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -192,33 +200,33 @@ export function EntryForm({ onSuccess, initialData }: EntryFormProps) {
|
|||||||
<tr>
|
<tr>
|
||||||
{/* Symbol */}
|
{/* Symbol */}
|
||||||
<td>
|
<td>
|
||||||
<input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
name="symbol"
|
name="symbol"
|
||||||
required
|
required
|
||||||
value={formData.symbol}
|
value={formData.symbol}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
placeholder="BTC/USD"
|
placeholder="BTC/USD"
|
||||||
className="bg-transparent border-b border-gray-600 focus:border-blue-500 outline-none w-full py-1 text-sm text-gray-200 placeholder-gray-600"
|
className={tableInputClass}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
{/* Label */}
|
{/* Label */}
|
||||||
<td>
|
<td>
|
||||||
<input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
name="label"
|
name="label"
|
||||||
value={formData.label}
|
value={formData.label}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
placeholder="Swing"
|
placeholder="Swing"
|
||||||
className="bg-transparent border-b border-gray-600 focus:border-blue-500 outline-none w-full py-1 text-sm text-gray-200 placeholder-gray-600"
|
className={tableInputClass}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
{/* Options (Real/Crypto) */}
|
{/* Options (Real/Crypto) */}
|
||||||
<td>
|
<td>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<label className="flex items-center cursor-pointer text-xs text-yellow-400 hover:text-yellow-300" title="Places actual order on Bot">
|
<label className="flex cursor-pointer items-center text-xs text-amber-600 hover:text-amber-500 dark:text-amber-400 dark:hover:text-amber-300" title="Places actual order on Bot">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="execute_order"
|
name="execute_order"
|
||||||
@ -226,10 +234,10 @@ export function EntryForm({ onSuccess, initialData }: EntryFormProps) {
|
|||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="mr-1.5 accent-yellow-500"
|
className="mr-1.5 accent-yellow-500"
|
||||||
/>
|
/>
|
||||||
⚡ Execute
|
Execute
|
||||||
</label>
|
</label>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<label className="flex items-center cursor-pointer text-[10px] text-gray-500 hover:text-gray-300" title="Marks as Active Trade in Journal">
|
<label className={checkboxLabelClass} title="Marks as Active Trade in Journal">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="is_real_trade"
|
name="is_real_trade"
|
||||||
@ -239,7 +247,7 @@ export function EntryForm({ onSuccess, initialData }: EntryFormProps) {
|
|||||||
/>
|
/>
|
||||||
Trade
|
Trade
|
||||||
</label>
|
</label>
|
||||||
<label className="flex items-center cursor-pointer text-[10px] text-gray-500 hover:text-gray-300">
|
<label className={checkboxLabelClass}>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="is_crypto"
|
name="is_crypto"
|
||||||
@ -255,76 +263,76 @@ export function EntryForm({ onSuccess, initialData }: EntryFormProps) {
|
|||||||
|
|
||||||
{/* Buy Price */}
|
{/* Buy Price */}
|
||||||
<td>
|
<td>
|
||||||
<input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
name="buy_price"
|
name="buy_price"
|
||||||
step="any"
|
step="any"
|
||||||
value={formData.buy_price}
|
value={formData.buy_price}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="bg-transparent border-b border-gray-600 focus:border-green-500 outline-none w-full py-1 text-sm text-right text-green-400 placeholder-gray-700 font-mono"
|
className={`${numericInputClass} text-emerald-600 dark:text-emerald-400`}
|
||||||
placeholder="0.00"
|
placeholder="0.00"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
{/* Qty */}
|
{/* Qty */}
|
||||||
<td>
|
<td>
|
||||||
<input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
name="quantity"
|
name="quantity"
|
||||||
step="any"
|
step="any"
|
||||||
value={formData.quantity}
|
value={formData.quantity}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="bg-transparent border-b border-gray-600 focus:border-blue-500 outline-none w-full py-1 text-sm text-right text-gray-200 placeholder-gray-700 font-mono"
|
className={numericInputClass}
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
{/* SL */}
|
{/* SL */}
|
||||||
<td>
|
<td>
|
||||||
<input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
name="drop_threshold_for_buy"
|
name="drop_threshold_for_buy"
|
||||||
step="any"
|
step="any"
|
||||||
value={formData.drop_threshold_for_buy}
|
value={formData.drop_threshold_for_buy}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="bg-transparent border-b border-gray-600 focus:border-red-500 outline-none w-full py-1 text-sm text-right text-red-400 placeholder-gray-700 font-mono"
|
className={`${numericInputClass} text-red-600 dark:text-red-400`}
|
||||||
placeholder="SL"
|
placeholder="SL"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
{/* TP */}
|
{/* TP */}
|
||||||
<td>
|
<td>
|
||||||
<input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
name="gain_threshold_for_sell"
|
name="gain_threshold_for_sell"
|
||||||
step="any"
|
step="any"
|
||||||
value={formData.gain_threshold_for_sell}
|
value={formData.gain_threshold_for_sell}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="bg-transparent border-b border-gray-600 focus:border-blue-500 outline-none w-full py-1 text-sm text-right text-blue-400 placeholder-gray-700 font-mono"
|
className={`${numericInputClass} text-blue-600 dark:text-blue-400`}
|
||||||
placeholder="TP"
|
placeholder="TP"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
{/* Time */}
|
{/* Time */}
|
||||||
<td>
|
<td>
|
||||||
<input
|
<Input
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
name="buy_time"
|
name="buy_time"
|
||||||
value={formData.buy_time ? new Date(formData.buy_time).toISOString().slice(0, 16) : ''}
|
value={formData.buy_time ? new Date(formData.buy_time).toISOString().slice(0, 16) : ''}
|
||||||
onChange={(e) => setFormData(prev => ({ ...prev, buy_time: new Date(e.target.value).toISOString() }))}
|
onChange={(e) => setFormData(prev => ({ ...prev, buy_time: new Date(e.target.value).toISOString() }))}
|
||||||
className="bg-transparent border-b border-gray-600 focus:border-blue-500 outline-none w-full py-1 text-xs text-gray-400 font-mono"
|
className="h-9 rounded border-[var(--border)] bg-[var(--input)] px-2 py-1 font-mono text-xs"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
{/* Sell Price (Exit) */}
|
{/* Sell Price (Exit) */}
|
||||||
<td className="relative">
|
<td className="relative">
|
||||||
<input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
name="sell_price"
|
name="sell_price"
|
||||||
step="any"
|
step="any"
|
||||||
value={formData.sell_price}
|
value={formData.sell_price}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="bg-transparent border-b border-gray-600 focus:border-purple-500 outline-none w-full py-1 text-sm text-right text-purple-400 placeholder-gray-700 font-mono"
|
className={`${numericInputClass} text-purple-600 dark:text-purple-400`}
|
||||||
placeholder="Exit"
|
placeholder="Exit"
|
||||||
/>
|
/>
|
||||||
{formData.sell_price && formData.buy_price && (
|
{formData.sell_price && formData.buy_price && (
|
||||||
@ -341,33 +349,36 @@ export function EntryForm({ onSuccess, initialData }: EntryFormProps) {
|
|||||||
<td>
|
<td>
|
||||||
<div className="flex items-center justify-end gap-2">
|
<div className="flex items-center justify-end gap-2">
|
||||||
{initialData && (
|
{initialData && (
|
||||||
<button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onSuccess}
|
onClick={onSuccess}
|
||||||
className="text-xs text-gray-500 hover:text-white underline"
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="h-8 text-xs underline"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="bg-blue-600 hover:bg-blue-500 text-white text-xs px-3 py-1.5 rounded font-medium transition-colors"
|
size="sm"
|
||||||
|
className="h-8 text-xs"
|
||||||
>
|
>
|
||||||
{initialData ? 'Update' : 'Add'}
|
{initialData ? 'Update' : 'Add'}
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/* Notes Row */}
|
{/* Notes Row */}
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan={8} className="pt-2 border-t border-gray-800">
|
<td colSpan={10} className="border-t border-[var(--border)] pt-2">
|
||||||
<input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
name="notes"
|
name="notes"
|
||||||
value={formData.notes}
|
value={formData.notes}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
placeholder="Add strategy notes here..."
|
placeholder="Add strategy notes here..."
|
||||||
className="bg-transparent w-full text-xs text-gray-400 italic outline-none placeholder-gray-700"
|
className="h-9 border-transparent bg-transparent px-0 text-xs italic shadow-none focus:border-transparent focus:ring-0"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user