refactor(ui): polish bot config settings
This commit is contained in:
parent
92e717509d
commit
944ae498d2
@ -301,15 +301,23 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
|
||||
({ className, label, id, ...props }, ref) => {
|
||||
const generatedId = React.useId();
|
||||
const checkboxId = id ?? (label ? `checkbox-${generatedId}` : undefined);
|
||||
const input = (
|
||||
<input
|
||||
ref={ref}
|
||||
id={checkboxId}
|
||||
type="checkbox"
|
||||
className={cn('h-4 w-4 rounded border-[var(--border)] bg-[var(--card)] text-[var(--primary)] focus-visible:ring-2 focus-visible:ring-[var(--ring-soft)]', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
if (!label) {
|
||||
return input;
|
||||
}
|
||||
|
||||
return (
|
||||
<label className="inline-flex items-center gap-2 text-sm text-[var(--foreground)]">
|
||||
<input
|
||||
ref={ref}
|
||||
id={checkboxId}
|
||||
type="checkbox"
|
||||
className={cn('h-4 w-4 rounded border-[var(--border)] bg-[var(--card)] text-[var(--primary)] focus-visible:ring-2 focus-visible:ring-[var(--ring-soft)]', className)}
|
||||
{...props}
|
||||
/>
|
||||
{input}
|
||||
{label}
|
||||
</label>
|
||||
);
|
||||
|
||||
@ -549,6 +549,282 @@ body {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.settings-config-stack {
|
||||
display: grid;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.settings-access-panel,
|
||||
.settings-loading-state,
|
||||
.settings-action-panel,
|
||||
.settings-control-panel,
|
||||
.settings-info-footer {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--bl-radius-card);
|
||||
background: var(--card);
|
||||
box-shadow: var(--card-shadow);
|
||||
}
|
||||
|
||||
.settings-access-panel {
|
||||
border-color: color-mix(in oklab, var(--bl-danger) 26%, var(--border));
|
||||
background: color-mix(in oklab, var(--bl-danger) 8%, var(--card));
|
||||
padding: 28px;
|
||||
}
|
||||
|
||||
.settings-access-panel h2 {
|
||||
margin: 8px 0 0;
|
||||
color: var(--foreground);
|
||||
font-size: 22px;
|
||||
font-weight: 780;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.settings-access-panel p:not(.settings-panel-eyebrow) {
|
||||
margin: 10px 0 0;
|
||||
color: var(--muted-foreground);
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.settings-panel-eyebrow {
|
||||
margin: 0;
|
||||
color: var(--bl-danger);
|
||||
font-size: 11px;
|
||||
font-weight: 850;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.settings-loading-state {
|
||||
min-height: 260px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
align-content: center;
|
||||
gap: 14px;
|
||||
color: var(--muted-foreground);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.settings-loading-state p {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.settings-loading-spinner {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 3px solid var(--border);
|
||||
border-top-color: var(--accent);
|
||||
border-radius: 999px;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
.settings-feedback {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 16px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.settings-feedback--success {
|
||||
border-color: color-mix(in oklab, var(--bl-success) 28%, var(--border));
|
||||
background: color-mix(in oklab, var(--bl-success) 10%, var(--card));
|
||||
color: var(--bl-success);
|
||||
}
|
||||
|
||||
.settings-feedback--error {
|
||||
border-color: color-mix(in oklab, var(--bl-danger) 28%, var(--border));
|
||||
background: color-mix(in oklab, var(--bl-danger) 10%, var(--card));
|
||||
color: var(--bl-danger);
|
||||
}
|
||||
|
||||
.settings-feedback-icon {
|
||||
display: grid;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
flex: 0 0 auto;
|
||||
place-items: center;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in oklab, currentColor 12%, transparent);
|
||||
}
|
||||
|
||||
.settings-feedback span {
|
||||
display: block;
|
||||
color: var(--foreground);
|
||||
font-size: 14px;
|
||||
font-weight: 750;
|
||||
}
|
||||
|
||||
.settings-feedback p {
|
||||
margin: 4px 0 0;
|
||||
color: var(--muted-foreground);
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.settings-action-panel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.settings-action-heading {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.settings-action-icon,
|
||||
.settings-info-icon {
|
||||
display: grid;
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
flex: 0 0 auto;
|
||||
place-items: center;
|
||||
border: 1px solid color-mix(in oklab, var(--accent) 18%, var(--border));
|
||||
border-radius: 16px;
|
||||
background: color-mix(in oklab, var(--accent) 10%, var(--card));
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.settings-action-heading h2,
|
||||
.settings-control-header h3,
|
||||
.settings-info-content h5 {
|
||||
margin: 0;
|
||||
color: var(--foreground);
|
||||
font-weight: 760;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.settings-action-heading h2 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.settings-action-heading p,
|
||||
.settings-control-header p,
|
||||
.settings-info-content p {
|
||||
margin: 6px 0 0;
|
||||
color: var(--muted-foreground);
|
||||
font-size: 13px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.settings-action-controls {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.settings-control-panel {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.settings-control-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.settings-control-header h3,
|
||||
.settings-info-content h5 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.settings-toggle-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.settings-toggle-card {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
min-width: 0;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 16px;
|
||||
background: var(--card-elevated);
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.settings-toggle-card > span {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.settings-toggle-card span span {
|
||||
display: block;
|
||||
color: var(--foreground);
|
||||
font-size: 13px;
|
||||
font-weight: 750;
|
||||
}
|
||||
|
||||
.settings-toggle-card small {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
color: var(--muted-foreground);
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.settings-info-footer {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.settings-info-watermark {
|
||||
position: absolute;
|
||||
top: -34px;
|
||||
right: 18px;
|
||||
opacity: 0.05;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.settings-info-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.settings-info-content strong {
|
||||
color: var(--foreground);
|
||||
font-weight: 760;
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.settings-action-panel,
|
||||
.settings-control-header,
|
||||
.settings-info-content {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.settings-action-controls {
|
||||
justify-content: stretch;
|
||||
}
|
||||
|
||||
.settings-action-controls .product-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.settings-toggle-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.ux-field-label {
|
||||
color: var(--muted-foreground);
|
||||
font-size: 11px;
|
||||
|
||||
@ -51,9 +51,9 @@ describe('ConfigTab DOM behavior', () => {
|
||||
await user.type(modeTextarea, 'disabled');
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Commit Changes' })).toBeEnabled();
|
||||
expect(screen.getByRole('button', { name: 'Abort Changes' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'Reset changes' })).toBeInTheDocument();
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'Abort Changes' }));
|
||||
await user.click(screen.getByRole('button', { name: 'Reset changes' }));
|
||||
expect(screen.getByDisplayValue('enabled')).toBeInTheDocument();
|
||||
|
||||
const resetModeTextarea = screen.getByDisplayValue('enabled');
|
||||
@ -63,7 +63,7 @@ describe('ConfigTab DOM behavior', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(upsertDynamicConfigItemsMock).toHaveBeenCalledTimes(1);
|
||||
expect(screen.getByText('SYNC COMPLETE')).toBeInTheDocument();
|
||||
expect(screen.getByText('Saved')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(upsertDynamicConfigItemsMock.mock.calls[0][0]).toEqual(
|
||||
@ -99,7 +99,7 @@ describe('ConfigTab DOM behavior', () => {
|
||||
await user.click(screen.getByRole('button', { name: 'Commit Changes' }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('CRITICAL ERROR')).toBeInTheDocument();
|
||||
expect(screen.getByText('Update failed')).toBeInTheDocument();
|
||||
expect(screen.getByText(/Sync failed: write failed/)).toBeInTheDocument();
|
||||
});
|
||||
}, 20000);
|
||||
@ -109,7 +109,7 @@ describe('ConfigTab DOM behavior', () => {
|
||||
|
||||
render(<ConfigTab />);
|
||||
|
||||
expect(screen.getByText(/Access Denied/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/Access denied/i)).toBeInTheDocument();
|
||||
expect(fetchDynamicConfigItemsMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@ -4,6 +4,7 @@ import { clearBacktestRuntimeFlagCache } from '../backtest/flags';
|
||||
import { fetchDynamicConfigItems, upsertDynamicConfigItems } from '../lib/dynamicConfigApi';
|
||||
import { useAuth } from '../components/AuthContext';
|
||||
import { BACKTEST_FLAG_KEYS } from '../../../shared/feature-flags.js';
|
||||
import { Button, Checkbox, Textarea } from '../components/ui/Primitives';
|
||||
|
||||
interface ConfigItem {
|
||||
key: string;
|
||||
@ -102,7 +103,7 @@ export const ConfigTab = () => {
|
||||
await upsertDynamicConfigItems(buildConfigUpsertPayload(configs));
|
||||
|
||||
clearBacktestRuntimeFlagCache();
|
||||
setMessage({ type: 'success', text: 'Neural parameters synchronized successfully.' });
|
||||
setMessage({ type: 'success', text: 'Runtime configuration updated successfully.' });
|
||||
setOriginalConfigs(cloneConfigItems(configs));
|
||||
setTimeout(() => setMessage(null), 4000);
|
||||
} catch (err: any) {
|
||||
@ -142,99 +143,91 @@ export const ConfigTab = () => {
|
||||
|
||||
if (!isAdmin) {
|
||||
return (
|
||||
<div className="bg-[var(--card-elevated)] border border-red-500/20 p-8 rounded-3xl">
|
||||
<div className="max-w-xl">
|
||||
<p className="text-[11px] font-black uppercase tracking-[0.3em] text-red-400">Restricted</p>
|
||||
<h2 className="text-2xl font-black text-white mt-3">Access Denied</h2>
|
||||
<p className="text-sm text-zinc-400 mt-3">
|
||||
Global bot configuration is limited to administrator accounts.
|
||||
</p>
|
||||
</div>
|
||||
<div className="settings-access-panel">
|
||||
<p className="settings-panel-eyebrow">Restricted</p>
|
||||
<h2>Access denied</h2>
|
||||
<p>Global bot configuration is limited to administrator accounts.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-64 text-gray-500 animate-pulse">
|
||||
<div className="w-10 h-10 border-4 border-[var(--bl-success-muted)] border-t-[var(--bl-success)] rounded-full animate-spin mb-4 shadow-xl" />
|
||||
<p className="text-xs font-black uppercase tracking-[0.3em]">Calibrating Infrastructure...</p>
|
||||
<div className="settings-loading-state" role="status">
|
||||
<div className="settings-loading-spinner" aria-hidden="true" />
|
||||
<p>Loading runtime configuration...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-10 animate-in fade-in duration-700">
|
||||
{/* 1. NOTIFICATION PANEL */}
|
||||
<div className="settings-config-stack animate-in fade-in duration-700">
|
||||
{message && (
|
||||
<div className={`p-6 rounded-2xl flex items-center gap-4 border-2 animate-in slide-in-from-top-4 duration-500 ${message.type === 'success' ? 'bg-green-500/10 border-green-500/20 text-green-400' : 'bg-red-500/10 border-red-500/20 text-red-400'
|
||||
}`}>
|
||||
<div className={`p-2 rounded-full ${message.type === 'success' ? 'bg-green-400/20' : 'bg-red-400/20'}`}>
|
||||
<div className={`settings-feedback settings-feedback--${message.type} animate-in slide-in-from-top-4 duration-500`}>
|
||||
<div className="settings-feedback-icon">
|
||||
{message.type === 'success' ? <CheckCircle2 size={24} /> : <AlertCircle size={24} />}
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-sm font-black uppercase tracking-widest">{message.type === 'success' ? 'SYNC COMPLETE' : 'CRITICAL ERROR'}</span>
|
||||
<p className="text-xs font-bold opacity-80 uppercase tracking-tight leading-none mt-1">{message.text}</p>
|
||||
<span>{message.type === 'success' ? 'Saved' : 'Update failed'}</span>
|
||||
<p>{message.text}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 2. ACTION CONTROL CENTER */}
|
||||
<div className="flex flex-col md:flex-row md:items-center justify-between gap-6 bg-[var(--card-elevated)] border border-white/5 p-8 rounded-3xl relative overflow-hidden group shadow-2xl">
|
||||
<div className="relative z-10 flex items-center gap-6">
|
||||
<div className="w-14 h-14 bg-white/5 border border-white/10 rounded-2xl flex items-center justify-center text-[var(--bl-info-strong)] shadow-xl">
|
||||
<div className="settings-action-panel">
|
||||
<div className="settings-action-heading">
|
||||
<div className="settings-action-icon">
|
||||
<Zap size={30} fill="currentColor" fillOpacity={0.1} />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-xl font-black text-white flex items-center gap-3 uppercase tracking-tight">
|
||||
Global Operational Parameters
|
||||
</h2>
|
||||
<p className="text-gray-500 text-[10px] font-bold uppercase tracking-[0.2em] opacity-60 mt-2">Core Infrastructure Configuration & Credentials</p>
|
||||
<h2>Bot Runtime Configuration</h2>
|
||||
<p>Core infrastructure settings, providers, and guarded credentials.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 relative z-10">
|
||||
<div className="settings-action-controls">
|
||||
{hasChanges && (
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
onClick={handleReset}
|
||||
className="px-4 py-2 text-[10px] font-black uppercase tracking-widest text-gray-500 hover:text-white transition-all underline underline-offset-8 decoration-white/10"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
>
|
||||
Abort Changes
|
||||
</button>
|
||||
Reset changes
|
||||
</Button>
|
||||
)}
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
onClick={handleSaveAll}
|
||||
disabled={!hasChanges || saving}
|
||||
className={`flex items-center gap-3 px-10 py-4 rounded-2xl text-[10px] font-black uppercase tracking-widest transition-all shadow-2xl ${hasChanges && !saving
|
||||
? 'bg-white text-black hover:bg-[var(--bl-info-strong)] transition-colors'
|
||||
: 'bg-white/5 text-gray-600 cursor-not-allowed'
|
||||
}`}
|
||||
size="lg"
|
||||
>
|
||||
{saving ? 'Syncing...' : 'Commit Changes'}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-[var(--card-elevated)] border border-white/5 p-6 rounded-3xl space-y-4">
|
||||
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
|
||||
<div className="settings-control-panel">
|
||||
<div className="settings-control-header">
|
||||
<div>
|
||||
<h3 className="text-sm font-black text-white uppercase tracking-widest">Backtest Access Control</h3>
|
||||
<p className="text-[10px] text-zinc-500 uppercase tracking-wider mt-1">
|
||||
<h3>Backtest Access Control</h3>
|
||||
<p>
|
||||
Runtime flags in <code>bot_config</code>. Applies automatically on dynamic config refresh.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
onClick={handleSaveBacktestFlags}
|
||||
disabled={savingBacktestFlags}
|
||||
className={`px-5 py-2 rounded-xl text-[10px] font-black uppercase tracking-widest ${savingBacktestFlags ? 'bg-white/5 text-zinc-600 cursor-not-allowed' : 'bg-[var(--bl-success)] text-black hover:opacity-90'}`}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
>
|
||||
{savingBacktestFlags ? 'Saving...' : 'Save Backtest Flags'}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<label className="flex items-start gap-3 rounded-2xl border border-white/10 bg-black/20 p-4">
|
||||
<input
|
||||
type="checkbox"
|
||||
<div className="settings-toggle-grid">
|
||||
<label className="settings-toggle-card">
|
||||
<Checkbox
|
||||
checked={backtestFlags.enableBacktest}
|
||||
onChange={(event) => setBacktestFlags((prev) => ({
|
||||
...prev,
|
||||
@ -243,26 +236,24 @@ export const ConfigTab = () => {
|
||||
}))}
|
||||
/>
|
||||
<span>
|
||||
<span className="block text-[11px] font-black text-white uppercase tracking-wide">Enable Backtest Globally</span>
|
||||
<span className="block text-[10px] text-zinc-500 mt-1">Master switch for `/api/backtest/run`.</span>
|
||||
<span>Enable Backtest Globally</span>
|
||||
<small>Master switch for `/api/backtest/run`.</small>
|
||||
</span>
|
||||
</label>
|
||||
<label className="flex items-start gap-3 rounded-2xl border border-white/10 bg-black/20 p-4">
|
||||
<input
|
||||
type="checkbox"
|
||||
<label className="settings-toggle-card">
|
||||
<Checkbox
|
||||
checked={backtestFlags.customerEnabled}
|
||||
disabled={!backtestFlags.enableBacktest}
|
||||
onChange={(event) => setBacktestFlags((prev) => ({ ...prev, customerEnabled: event.target.checked }))}
|
||||
/>
|
||||
<span>
|
||||
<span className="block text-[11px] font-black text-white uppercase tracking-wide">Enable Backtest For Customers</span>
|
||||
<span className="block text-[10px] text-zinc-500 mt-1">When off, only admin users can run backtests.</span>
|
||||
<span>Enable Backtest For Customers</span>
|
||||
<small>When off, only admin users can run backtests.</small>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 3. RESPONSIVE TABLE OF CONFIGS */}
|
||||
<div className="table-container overflow-x-auto">
|
||||
<table className="pro-table w-full">
|
||||
<thead>
|
||||
@ -295,7 +286,7 @@ export const ConfigTab = () => {
|
||||
</td>
|
||||
<td className="px-6 py-6">
|
||||
<div className="relative group/field">
|
||||
<textarea
|
||||
<Textarea
|
||||
value={config.value}
|
||||
onChange={(e) => handleChange(config.key, e.target.value)}
|
||||
className={`w-full bg-black/40 border border-white/5 rounded-2xl px-5 py-4 text-[12px] font-mono font-bold focus:outline-none transition-all min-h-[80px] hover:border-white/10 ${isSecret ? 'text-orange-400 focus:border-orange-500' : 'text-blue-400 focus:border-blue-500'}`}
|
||||
@ -321,20 +312,19 @@ export const ConfigTab = () => {
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* 4. IMMUTABLE FOOTER INFO */}
|
||||
<footer className="relative p-12 bg-[var(--bl-surface-overlay)] border border-white/5 rounded-[3rem] overflow-hidden group shadow-2xl">
|
||||
<div className="absolute top-0 right-0 p-12 opacity-[0.03] group-hover:opacity-10 transition-all duration-1000">
|
||||
<footer className="settings-info-footer">
|
||||
<div className="settings-info-watermark">
|
||||
<ShieldAlert size={160} className="text-blue-400" />
|
||||
</div>
|
||||
<div className="relative z-10 flex flex-col lg:flex-row gap-10 items-center">
|
||||
<div className="p-8 bg-blue-500/5 rounded-3xl text-blue-400 border border-blue-500/10 shadow-inner">
|
||||
<div className="settings-info-content">
|
||||
<div className="settings-info-icon">
|
||||
<Info size={40} />
|
||||
</div>
|
||||
<div className="space-y-4 max-w-4xl text-center lg:text-left">
|
||||
<h5 className="text-xs font-black text-white uppercase tracking-[0.5em] opacity-80">Synchronization Protocol Alpha</h5>
|
||||
<p className="text-[11px] text-gray-600 font-bold leading-relaxed uppercase tracking-[0.15em]">
|
||||
Updates to <span className="text-white/80 border-b border-blue-500/30 pb-0.5">SYMBOLS</span> and <span className="text-white/80 border-b border-blue-500/30 pb-0.5">CAPITAL</span> are hot-loaded within the active cycle.
|
||||
Changes to <span className="text-white/80 border-b border-blue-500/30 pb-0.5">ENCRYPTED PROVIDERS</span> or <span className="text-white/80 border-b border-blue-500/30 pb-0.5">API CREDENTIALS</span> require a hard handshake reset of the neural execution daemon to re establishment secure connections.
|
||||
<div>
|
||||
<h5>Runtime Update Behavior</h5>
|
||||
<p>
|
||||
Updates to <strong>symbols</strong> and <strong>capital</strong> are hot-loaded within the active cycle.
|
||||
Changes to <strong>provider credentials</strong> or <strong>API keys</strong> may require a service restart before every worker uses the new value.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -140,7 +140,7 @@ describe('dashboard tabs smoke coverage', () => {
|
||||
expect(adminHtml).toContain('AIAnalysis');
|
||||
|
||||
const configHtml = renderToStaticMarkup(React.createElement(ConfigTab));
|
||||
expect(configHtml).toContain('Calibrating Infrastructure');
|
||||
expect(configHtml).toContain('Loading runtime configuration');
|
||||
});
|
||||
|
||||
it('renders HistoryTab loading state shell', () => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user