Unify dialog styling with consistent gradient headers

- Add .dialog-header CSS class with blue gradient (#3E3AF2 → #6366f1)
- Add .dialog-close-btn for consistent close buttons in headers
- Update all 12 dialog templates to use unified header styling
- Add close buttons to all dialogs (positioned in header)
- Fix trade_details and login popups (add proper header structure)
- Update public strategy browser modal to match dialog style
- Update indicator popup styling to match app theme
- Add null checks to initializeResizablePopup for robustness
- Update CLAUDE.md with wallet module documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rob 2026-03-09 14:57:14 -03:00
parent 7e77f55837
commit a4422dc556
14 changed files with 192 additions and 62 deletions

View File

@ -107,6 +107,7 @@ Flask web application with SocketIO for real-time communication, using eventlet
| `mapped_strategy.py` | Backtrader strategy integration |
| `shared_utilities.py` | Time/date conversion utilities |
| `utils.py` | JSON serialization helpers |
| `exchange_validation.py` | Exchange requirements validation with structured error codes |
### EDM Client Module (`src/edm_client/`)
@ -127,6 +128,23 @@ Flask web application with SocketIO for real-time communication, using eventlet
| `live_broker.py` | Real exchange trading via ccxt |
| `factory.py` | Creates appropriate broker based on mode |
### Wallet Module (`src/wallet/`)
Bitcoin wallet system for strategy fees with two-address custody model.
| Module | Purpose |
|--------|---------|
| `wallet_manager.py` | Main wallet operations, credits ledger, fee accumulation/settlement |
| `bitcoin_service.py` | Bitcoin key generation, transaction creation, address validation |
| `encryption.py` | Versioned key encryption for stored private keys |
| `background_jobs.py` | Deposit detection, withdrawal processing, auto-sweep jobs |
**Key concepts:**
- **Fee Address**: Platform-controlled address for strategy fees (private key stored encrypted)
- **Sweep Address**: User-controlled address for excess funds (we only store public address)
- **Credits Ledger**: Internal balance for instant, reliable strategy fee deduction
- **Balance Cap**: ~$50 limit before auto-sweep to user's sweep address
### Key Paths
- **Source code**: `src/`

View File

@ -21,6 +21,54 @@ hr{
}
/*************** Popup forms *********************/
/* Unified dialog header */
.dialog-header {
cursor: move;
padding: 10px;
background: linear-gradient(135deg, #3E3AF2 0%, #6366f1 100%);
color: white;
border-bottom: 1px solid #ccc;
position: relative;
width: inherit;
height: fit-content;
}
.dialog-header h1 {
margin: 0;
font-size: 18px;
padding-right: 30px; /* Space for close button */
}
/* Warning variant (for wallet keys modal) */
.dialog-header.warning {
background: linear-gradient(135deg, #c00 0%, #e53935 100%);
}
/* Close button in header - centered vertically */
.dialog-close-btn {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
width: 24px;
height: 24px;
border-radius: 50%;
background: rgba(255,255,255,0.2);
border: none;
color: white;
font-size: 16px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s;
line-height: 1;
}
.dialog-close-btn:hover {
background: rgba(255,255,255,0.3);
}
/* The popup form */
.form-popup {
/* Hidden by default. */
@ -342,12 +390,37 @@ height: 500px;
display: none;
position: absolute;
left: 100px; top: 50px;
width: 135px;
padding: 10px;
background-color: rgb(200,100,100);
border: solid black 1px;
text-align: justify; font-size: 12px;
z-index:99;
width: 160px;
padding: 12px;
background: linear-gradient(135deg, #3E3AF2 0%, #6366f1 100%);
border: 1px solid #ccc;
border-radius: 8px;
text-align: left;
font-size: 12px;
color: white;
z-index: 99;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
#indicators label {
color: white;
margin-left: 5px;
}
#indicators input[type="submit"] {
margin-top: 10px;
width: 100%;
padding: 6px;
border: none;
border-radius: 4px;
background: rgba(255,255,255,0.2);
color: white;
cursor: pointer;
font-size: 12px;
}
#indicators input[type="submit"]:hover {
background: rgba(255,255,255,0.3);
}
#enable_indicators{
height:20px;

View File

@ -44,6 +44,10 @@ class User_Interface {
this.initializeResizablePopup("withdraw_modal", null, "withdraw_header", "resize-withdraw");
this.initializeResizablePopup("credit_modal", null, "credit_header", "resize-credit");
// Trade details and login popups
this.initializeResizablePopup("trade_details_form", null, "trade_details_header", "resize-trade-details");
this.initializeResizablePopup("login_popup", null, "login_draggable_header", null);
// Initialize Backtesting's DOM elements
this.backtesting.initialize();
} catch (error) {
@ -90,6 +94,10 @@ class User_Interface {
*/
initializeResizablePopup(popupId, resizeCallback = null, headerId = null, resizerId) {
const popupElement = document.getElementById(popupId);
if (!popupElement) {
console.warn(`initializeResizablePopup: Element '${popupId}' not found`);
return;
}
this.dragElement(popupElement, headerId);
this.makeResizable(popupElement, resizeCallback, resizerId);
}
@ -137,10 +145,14 @@ class User_Interface {
* Make an element resizable and optionally call a resize callback (like for Blockly workspace).
* @param {HTMLElement} elm - The element to make resizable.
* @param {function|null} resizeCallback - Optional callback to resize specific content.
* @param {string} resizerId - The ID of the resizer handle element.
* @param {string|null} resizerId - The ID of the resizer handle element, or null to skip resizing.
*/
makeResizable(elm, resizeCallback = null, resizerId) {
if (!resizerId) return; // Skip if no resizer specified
const resizer = document.getElementById(resizerId);
if (!resizer) return; // Skip if resizer element not found
let originalWidth = 0;
let originalHeight = 0;
let originalMouseX = 0;

View File

@ -2,8 +2,9 @@
<div class="form-popup" id="account_settings_form" style="display: none; overflow: hidden; position: absolute; width: 500px; height: 550px; border-radius: 10px;">
<!-- Draggable Header Section -->
<div id="account_settings_header" style="cursor: move; padding: 10px; background-color: #3E3AF2; color: white; border-bottom: 1px solid #ccc;">
<h1 style="margin: 0; font-size: 18px;">Account Settings</h1>
<div class="dialog-header" id="account_settings_header">
<h1>Account Settings</h1>
<button class="dialog-close-btn" onclick="UI.account.closeAccountSettings()">&times;</button>
</div>
<!-- Tabs -->
@ -129,17 +130,15 @@
</div>
</div>
<!-- Close Button -->
<div class="close-btn" onclick="UI.account.closeAccountSettings()">X</div>
<!-- Resizer -->
<div id="resize-account" class="resize-handle"></div>
</div>
<!-- Wallet Setup Modal with Terms -->
<div class="form-popup" id="wallet_setup_modal" style="display: none; overflow: hidden; position: absolute; width: 550px; height: 600px; border-radius: 10px;">
<div id="wallet_setup_header" style="cursor: move; padding: 10px; background-color: #3E3AF2; color: white; border-bottom: 1px solid #ccc;">
<h1 style="margin: 0; font-size: 18px;">Setup Bitcoin Wallet</h1>
<div class="dialog-header" id="wallet_setup_header">
<h1>Setup Bitcoin Wallet</h1>
<button class="dialog-close-btn" onclick="UI.account.closeSetupDialog()">&times;</button>
</div>
<div class="form-container" style="padding: 15px; overflow-y: auto; height: calc(100% - 60px);">
@ -211,14 +210,13 @@
</div>
</div>
<div class="close-btn" onclick="UI.account.closeSetupDialog()">X</div>
<div id="resize-wallet-setup" class="resize-handle"></div>
</div>
<!-- Keys Display Modal (shown after creation, requires acknowledgment) -->
<div class="form-popup" id="wallet_keys_modal" style="display: none; overflow: hidden; position: absolute; width: 550px; height: 500px; border-radius: 10px;">
<div id="wallet_keys_header" style="cursor: move; padding: 10px; background-color: #c00; color: white; border-bottom: 1px solid #ccc;">
<h1 style="margin: 0; font-size: 18px;">Your Wallet Keys - SAVE THESE NOW</h1>
<div class="dialog-header warning" id="wallet_keys_header">
<h1>Your Wallet Keys - SAVE THESE NOW</h1>
</div>
<div class="form-container" style="padding: 15px; overflow-y: auto; height: calc(100% - 60px);">
@ -286,8 +284,9 @@
<!-- View Keys Modal (for viewing fee address keys later) -->
<div class="form-popup" id="view_keys_modal" style="display: none; overflow: hidden; position: absolute; width: 450px; height: 320px; border-radius: 10px;">
<div id="view_keys_header" style="cursor: move; padding: 10px; background-color: #3E3AF2; color: white; border-bottom: 1px solid #ccc;">
<h1 style="margin: 0; font-size: 18px;">Fee Address Keys</h1>
<div class="dialog-header" id="view_keys_header">
<h1>Fee Address Keys</h1>
<button class="dialog-close-btn" onclick="UI.account.closeViewKeysDialog()">&times;</button>
</div>
<div class="form-container" style="padding: 15px; overflow-y: auto; height: calc(100% - 60px);">
@ -321,14 +320,14 @@
</div>
</div>
<div class="close-btn" onclick="UI.account.closeViewKeysDialog()">X</div>
<div id="resize-view-keys" class="resize-handle"></div>
</div>
<!-- Withdraw Dialog -->
<div class="form-popup" id="withdraw_modal" style="display: none; overflow: hidden; position: absolute; width: 400px; height: 250px; border-radius: 10px;">
<div id="withdraw_header" style="cursor: move; padding: 10px; background-color: #3E3AF2; color: white; border-bottom: 1px solid #ccc;">
<h1 style="margin: 0; font-size: 18px;">Withdraw BTC</h1>
<div class="dialog-header" id="withdraw_header">
<h1>Withdraw BTC</h1>
<button class="dialog-close-btn" onclick="UI.account.closeWithdrawDialog()">&times;</button>
</div>
<div class="form-container" style="padding: 15px;">
@ -346,14 +345,14 @@
</div>
</div>
<div class="close-btn" onclick="UI.account.closeWithdrawDialog()">X</div>
<div id="resize-withdraw" class="resize-handle"></div>
</div>
<!-- Test Credit Dialog -->
<div class="form-popup" id="credit_modal" style="display: none; overflow: hidden; position: absolute; width: 350px; height: 220px; border-radius: 10px;">
<div id="credit_header" style="cursor: move; padding: 10px; background-color: #3E3AF2; color: white; border-bottom: 1px solid #ccc;">
<h1 style="margin: 0; font-size: 18px;">Add Test Credit</h1>
<div class="dialog-header" id="credit_header">
<h1>Add Test Credit</h1>
<button class="dialog-close-btn" onclick="UI.account.closeCreditDialog()">&times;</button>
</div>
<div class="form-container" style="padding: 15px;">
@ -368,7 +367,6 @@
</div>
</div>
<div class="close-btn" onclick="UI.account.closeCreditDialog()">X</div>
<div id="resize-credit" class="resize-handle"></div>
</div>

View File

@ -2,8 +2,9 @@
<div class="form-popup" id="ai_strategy_form" style="display: none; overflow: hidden; position: absolute; width: 500px; height: 400px; border-radius: 10px; z-index: 1100;">
<!-- Draggable Header Section -->
<div id="ai_strategy_header" style="cursor: move; padding: 10px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-bottom: 1px solid #ccc;">
<h1 style="margin: 0; font-size: 18px;">Generate Strategy with AI</h1>
<div class="dialog-header" id="ai_strategy_header">
<h1>Generate Strategy with AI</h1>
<button class="dialog-close-btn" onclick="UI.strats.closeAIDialog()">&times;</button>
</div>
<!-- Main Content -->
@ -20,7 +21,7 @@
<!-- Loading State -->
<div id="ai_strategy_loading" style="display: none; text-align: center; padding: 20px;">
<div class="spinner" style="border: 3px solid #444; border-top: 3px solid #667eea; border-radius: 50%; width: 30px; height: 30px; animation: spin 1s linear infinite; margin: 0 auto;"></div>
<div class="spinner" style="border: 3px solid #444; border-top: 3px solid #3E3AF2; border-radius: 50%; width: 30px; height: 30px; animation: spin 1s linear infinite; margin: 0 auto;"></div>
<p style="margin-top: 10px; color: #888;">Generating strategy... This may take a moment.</p>
</div>
@ -31,7 +32,7 @@
<div style="text-align: center; margin-top: 15px;">
<button type="button" class="btn cancel" onclick="UI.strats.closeAIDialog()" style="margin-right: 10px;">Cancel</button>
<button type="button" class="btn next" id="ai_generate_btn" onclick="UI.strats.generateWithAI()"
style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border: none;">
style="background: linear-gradient(135deg, #3E3AF2 0%, #6366f1 100%); border: none;">
Generate Strategy
</button>
</div>

View File

@ -2,8 +2,9 @@
<div class="form-popup" id="backtest_form" style="display: none; overflow: hidden; position: absolute; width: 400px; height: 600px; border-radius: 10px;">
<!-- Draggable Header Section -->
<div id="backtest_draggable_header" style="cursor: move; padding: 10px; background-color: #3E3AF2; color: white; border-bottom: 1px solid #ccc;">
<div class="dialog-header" id="backtest_draggable_header">
<h1 id="backtest-form-header">Create New Backtest</h1>
<button class="dialog-close-btn" onclick="UI.backtesting.closeForm()">&times;</button>
</div>
<!-- Main Content (Scrollable) -->

View File

@ -2,8 +2,9 @@
<div class="form-popup" id="exchanges_config_form" style="display: none; overflow: hidden; position: absolute; width: 400px; height: 380px; border-radius: 10px;">
<!-- Draggable Header Section -->
<div id="exchange_draggable_header" style="cursor: move; padding: 10px; background-color: #3E3AF2; color: white; border-bottom: 1px solid #ccc;">
<h1 style="margin: 0; font-size: 18px;">Configure Exchanges</h1>
<div class="dialog-header" id="exchange_draggable_header">
<h1>Configure Exchanges</h1>
<button class="dialog-close-btn" onclick="UI.exchanges.closeForm()">&times;</button>
</div>
<!-- Main Content -->

View File

@ -6,21 +6,21 @@
<button class="btn" type="button" id="logout_button" name="logout" onclick="UI.users.logout()">
Sign out
</button>
<div class="form-popup" id="login_popup">
<div class="login_content">
<!Cancel Button>
<div class="close-btn" onclick="UI.users.toggleLogin()">
×</div>
<!Login Form>
<h1>Sign in</h1>
<div class="form-popup" id="login_popup" style="border-radius: 10px; overflow: hidden;">
<!-- Draggable Header Section -->
<div class="dialog-header" id="login_draggable_header">
<h1>Sign In</h1>
<button class="dialog-close-btn" onclick="UI.users.toggleLogin()">&times;</button>
</div>
<!-- Main Content -->
<div class="form-container login_content" style="padding: 15px;">
<form name="signup_form" action="/login" method="POST">
<div class="input-field"><input id='username' name='username' placeholder="Username" class="validate"></div>
<div class="input-field"><input type="password" id='password' name='password' placeholder="Password" class="validate"></div>
<button class="btn" onclick="UI.users.validate_input({username, email, password})">Sign in</button>
</form>
<p>Dont have an account? <a href="/signup">Sign Up</a></p>
<!Pop-up Button>
<p style="text-align: center; margin-top: 15px;">Don't have an account? <a href="/signup">Sign Up</a></p>
</div>
</div>
</div>

View File

@ -2,8 +2,9 @@
<div class="form-popup" id="new_ind_form" style="display: none; overflow: hidden; position: absolute; width: 420px; height: 520px; border-radius: 10px;">
<!-- Draggable Header Section -->
<div id="indicator_draggable_header" style="cursor: move; padding: 10px; background-color: #3E3AF2; color: white; border-bottom: 1px solid #ccc;">
<div class="dialog-header" id="indicator_draggable_header">
<h1>Create New Indicator</h1>
<button class="dialog-close-btn" onclick="UI.indicators.close_form()">&times;</button>
</div>
<!-- Main Content -->

View File

@ -2,8 +2,9 @@
<div class="form-popup" id="new_sig_form" style="display: none; overflow: hidden; position: absolute; width: 420px; height: 480px; border-radius: 10px;">
<!-- Draggable Header Section -->
<div id="signal_draggable_header" style="cursor: move; padding: 10px; background-color: #3E3AF2; color: white; border-bottom: 1px solid #ccc;">
<div class="dialog-header" id="signal_draggable_header">
<h1 id="signal-form-header">Add New Signal</h1>
<button class="dialog-close-btn" onclick="UI.signals.close_signal_Form()">&times;</button>
</div>
<!-- Main Content -->

View File

@ -2,9 +2,10 @@
<div class="form-popup" id="new_strat_form" style="display: none; overflow: hidden; position: absolute; width: 450px; height: 760px; border-radius: 10px;">
<!-- Draggable Header Section -->
<div id="draggable_header" style="cursor: move; padding: 10px; background-color: #3E3AF2; color: white; border-bottom: 1px solid #ccc;">
<div class="dialog-header" id="draggable_header">
<h1 id="form-header-create">Create New Strategy</h1>
<h1 id="form-header-edit" style="display: none;">Edit Strategy</h1>
<button class="dialog-close-btn" onclick="UI.strats.uiManager.hideForm()">&times;</button>
</div>
<!-- Main Content (Scrollable) -->
@ -12,7 +13,7 @@
<!-- AI Strategy Builder Button -->
<div style="grid-column: 1; text-align: center; margin: 10px 0;">
<button type="button" id="ai-generate-btn" onclick="UI.strats.openAIDialog()"
style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
style="background: linear-gradient(135deg, #3E3AF2 0%, #6366f1 100%);
color: white; border: none; padding: 8px 16px; border-radius: 5px;
cursor: pointer; font-size: 14px; font-weight: bold;">
Generate with AI

View File

@ -2,8 +2,9 @@
<div class="form-popup" id="new_trade_form" style="display: none; overflow: hidden; position: absolute; width: 400px; height: 480px; border-radius: 10px;">
<!-- Draggable Header Section -->
<div id="trade_draggable_header" style="cursor: move; padding: 10px; background-color: #3E3AF2; color: white; border-bottom: 1px solid #ccc;">
<div class="dialog-header" id="trade_draggable_header">
<h1>Create New Trade</h1>
<button class="dialog-close-btn" onclick="UI.trade.close_tradeForm()">&times;</button>
</div>
<!-- Main Content -->

View File

@ -280,27 +280,38 @@
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
border-bottom: 1px solid #ddd;
padding: 10px;
border-bottom: 1px solid #ccc;
background: linear-gradient(135deg, #3E3AF2 0%, #6366f1 100%);
color: white;
border-radius: 8px 8px 0 0;
}
.modal-header h2 {
margin: 0;
font-size: 18px;
color: white;
}
.modal-close {
background: none;
background: rgba(255,255,255,0.2);
border: none;
font-size: 24px;
border-radius: 50%;
width: 24px;
height: 24px;
font-size: 16px;
cursor: pointer;
color: #666;
color: white;
padding: 0;
line-height: 1;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s;
}
.modal-close:hover {
color: #333;
background: rgba(255,255,255,0.3);
}
.modal-body {

View File

@ -1,14 +1,21 @@
<!-- The popup for trade details -->
<div class="form-popup" id="trade_details_form">
<div class="form-popup" id="trade_details_form" style="display: none; overflow: hidden; position: absolute; border-radius: 10px;">
<!-- Draggable Header Section -->
<div class="dialog-header" id="trade_details_header">
<h1>Trade Details</h1>
<button class="dialog-close-btn" onclick="UI.trade.close_tradeDetails_Form()">&times;</button>
</div>
<!-- Main Content -->
<div class="form-container" style="padding: 15px;">
<div style="display: grid; grid-template-areas:
'a a a a a .'
'. b g c h .'
'. b g c h .'
'f f f f f f'
'. d i e j .'
'. d i e j .'
'z z z z z z';">
<h1 style="grid-area: a;">Trade_details</h1>
<div id='td_labels' style="grid-area: b;">
<p class = 'd_lbl'>ID:</p>
<p class = 'd_lbl'>Owner:</p>
@ -65,8 +72,12 @@
<p class = 'details' id="price_set">24,300</p>
<p class = 'details' id="val_set">53,987</p>
</div>
<div style="grid-area: z; text-align: center;margin-bottom:50px;">
<div style="grid-area: z; text-align: center; margin-top: 15px;">
<button style="margin-left:0;" type="button" class="btn cancel" onclick="UI.trade.close_tradeDetails_Form()">Close</button>
</div>
</div>
</div>
<!-- Resizer -->
<div id="resize-trade-details" class="resize-handle"></div>
</div>