Remove animated background (grid / orbs)

This commit is contained in:
Justin Oros
2026-01-28 16:22:59 -07:00
parent 7943440e54
commit 0dff4b0e5b
+46 -257
View File
@@ -20,13 +20,11 @@
--text-muted:#6e7681; --text-muted:#6e7681;
--border:#30363d; --border:#30363d;
} }
*{ *{
margin:0; margin:0;
padding:0; padding:0;
box-sizing:border-box; box-sizing:border-box;
} }
body{ body{
font-family:'Outfit',sans-serif; font-family:'Outfit',sans-serif;
background:var(--bg-primary); background:var(--bg-primary);
@@ -34,62 +32,6 @@
min-height:100vh; min-height:100vh;
overflow-x:hidden; overflow-x:hidden;
} }
/* Animated background grid */
.bg-grid {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
linear-gradient(var(--border) 1px, transparent 1px),
linear-gradient(90deg, var(--border) 1px, transparent 1px);
background-size: 50px 50px;
opacity: 0.3;
animation: gridMove 20s linear infinite;
pointer-events: none;
z-index: 0;
}
@keyframes gridMove {
0% { transform: translate(0, 0); }
100% { transform: translate(50px, 50px); }
}
/* Gradient orb effects */
.orb {
position: fixed;
border-radius: 50%;
filter: blur(80px);
opacity: 0.15;
pointer-events: none;
z-index: 0;
}
.orb-1 {
width: 300px;
height: 300px;
background: var(--accent-green);
top: -100px;
right: -100px;
animation: float 15s ease-in-out infinite;
}
.orb-2 {
width: 400px;
height: 400px;
background: var(--accent-blue);
bottom: -150px;
left: -150px;
animation: float 20s ease-in-out infinite reverse;
}
@keyframes float {
0%, 100% { transform: translate(0, 0) scale(1); }
50% { transform: translate(30px, -30px) scale(1.1); }
}
.container{ .container{
position:relative; position:relative;
z-index:1; z-index:1;
@@ -97,32 +39,26 @@
margin:0 auto; margin:0 auto;
padding:20px; padding:20px;
} }
/* Header */
header{ header{
text-align:center; text-align:center;
padding:40px 0 30px; padding:40px 0 30px;
animation: slideDown 0.6s ease-out; animation:slideDown .6s ease-out;
} }
@keyframes slideDown{ @keyframes slideDown{
from{opacity:0;transform:translateY(-20px);} from{opacity:0;transform:translateY(-20px);}
to{opacity:1;transform:translateY(0);} to{opacity:1;transform:translateY(0);}
} }
.logo-container{ .logo-container{
display:flex; display:flex;
flex-direction:column; flex-direction:column;
align-items:center; align-items:center;
gap:16px; gap:16px;
} }
.logo-image{ .logo-image{
width:clamp(180px,30vw,200px); width:clamp(180px,30vw,200px);
height:auto; height:auto;
filter: drop-shadow(0 4px 12px rgba(0, 255, 136, 0.3)); filter:drop-shadow(0 4px 12px rgba(0,255,136,.3));
} }
.logo{ .logo{
font-family:'JetBrains Mono',monospace; font-family:'JetBrains Mono',monospace;
font-size:clamp(28px,6vw,42px); font-size:clamp(28px,6vw,42px);
@@ -133,14 +69,11 @@
background-clip:text; background-clip:text;
letter-spacing:-1px; letter-spacing:-1px;
} }
.tagline{ .tagline{
font-size:clamp(14px,3vw,16px); font-size:clamp(14px,3vw,16px);
color:var(--text-secondary); color:var(--text-secondary);
font-weight:300; font-weight:300;
} }
/* Login Card */
.login-card{ .login-card{
background:var(--bg-card); background:var(--bg-card);
border:1px solid var(--border); border:1px solid var(--border);
@@ -148,37 +81,30 @@
padding:clamp(24px,5vw,40px); padding:clamp(24px,5vw,40px);
margin:20px auto; margin:20px auto;
max-width:500px; max-width:500px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); box-shadow:0 8px 32px rgba(0,0,0,.4);
animation: fadeInUp 0.6s ease-out 0.2s backwards; animation:fadeInUp .6s ease-out .2s backwards;
} }
@keyframes fadeInUp{ @keyframes fadeInUp{
from{opacity:0;transform:translateY(20px);} from{opacity:0;transform:translateY(20px);}
to{opacity:1;transform:translateY(0);} to{opacity:1;transform:translateY(0);}
} }
/* Trading Mode Toggle */
.trading-mode-group{ .trading-mode-group{
margin-bottom:24px; margin-bottom:24px;
} }
.mode-options{ .mode-options{
display:grid; display:grid;
grid-template-columns:1fr 1fr; grid-template-columns:1fr 1fr;
gap:12px; gap:12px;
} }
.mode-option{ .mode-option{
position:relative; position:relative;
} }
.mode-option input[type="radio"]{ .mode-option input[type="radio"]{
position:absolute; position:absolute;
opacity:0; opacity:0;
width:0; width:0;
height:0; height:0;
} }
.mode-option-label{ .mode-option-label{
display:flex; display:flex;
flex-direction:column; flex-direction:column;
@@ -189,19 +115,16 @@
border:2px solid var(--border); border:2px solid var(--border);
border-radius:8px; border-radius:8px;
cursor:pointer; cursor:pointer;
transition: all 0.3s ease; transition:all .3s ease;
} }
.mode-option input[type="radio"]:checked+.mode-option-label{ .mode-option input[type="radio"]:checked+.mode-option-label{
background: rgba(0, 255, 136, 0.1); background:rgba(0,255,136,.1);
border-color:var(--accent-green); border-color:var(--accent-green);
box-shadow: 0 0 0 3px rgba(0, 255, 136, 0.1); box-shadow:0 0 0 3px rgba(0,255,136,.1);
} }
.mode-option-label:hover{ .mode-option-label:hover{
border-color:var(--accent-green); border-color:var(--accent-green);
} }
.mode-title{ .mode-title{
font-family:'JetBrains Mono',monospace; font-family:'JetBrains Mono',monospace;
font-size:14px; font-size:14px;
@@ -209,17 +132,14 @@
color:var(--text-primary); color:var(--text-primary);
margin-bottom:4px; margin-bottom:4px;
} }
.mode-description{ .mode-description{
font-size:11px; font-size:11px;
color:var(--text-muted); color:var(--text-muted);
text-align:center; text-align:center;
} }
.input-group{ .input-group{
margin-bottom:20px; margin-bottom:20px;
} }
label{ label{
display:block; display:block;
font-size:13px; font-size:13px;
@@ -227,11 +147,9 @@
color:var(--text-secondary); color:var(--text-secondary);
margin-bottom:8px; margin-bottom:8px;
text-transform:uppercase; text-transform:uppercase;
letter-spacing: 0.5px; letter-spacing:.5px;
} }
input[type="password"],input[type="text"]{
input[type="password"],
input[type="text"] {
width:100%; width:100%;
padding:14px 16px; padding:14px 16px;
background:var(--bg-secondary); background:var(--bg-secondary);
@@ -240,22 +158,17 @@
color:var(--text-primary); color:var(--text-primary);
font-family:'JetBrains Mono',monospace; font-family:'JetBrains Mono',monospace;
font-size:14px; font-size:14px;
transition: all 0.3s ease; transition:all .3s ease;
} }
input[type="password"]:focus,input[type="text"]:focus{
input[type="password"]:focus,
input[type="text"]:focus {
outline:none; outline:none;
border-color:var(--accent-green); border-color:var(--accent-green);
box-shadow: 0 0 0 3px rgba(0, 255, 136, 0.1); box-shadow:0 0 0 3px rgba(0,255,136,.1);
} }
/* Make text input look like password field */
input[type="text"]#apiKey{ input[type="text"]#apiKey{
-webkit-text-security:disc; -webkit-text-security:disc;
text-security:disc; text-security:disc;
} }
.btn-go{ .btn-go{
width:100%; width:100%;
padding:16px; padding:16px;
@@ -269,48 +182,39 @@
cursor:pointer; cursor:pointer;
text-transform:uppercase; text-transform:uppercase;
letter-spacing:1px; letter-spacing:1px;
transition: all 0.3s ease; transition:all .3s ease;
box-shadow: 0 4px 16px rgba(0, 255, 136, 0.3); box-shadow:0 4px 16px rgba(0,255,136,.3);
} }
.btn-go:hover{ .btn-go:hover{
transform:translateY(-2px); transform:translateY(-2px);
box-shadow: 0 6px 24px rgba(0, 255, 136, 0.4); box-shadow:0 6px 24px rgba(0,255,136,.4);
} }
.btn-go:active{ .btn-go:active{
transform:translateY(0); transform:translateY(0);
} }
.btn-go:disabled{ .btn-go:disabled{
opacity: 0.5; opacity:.5;
cursor:not-allowed; cursor:not-allowed;
} }
.api-key-info{ .api-key-info{
text-align:center; text-align:center;
margin-top:12px; margin-top:12px;
font-size:11px; font-size:11px;
color:var(--text-muted); color:var(--text-muted);
} }
.api-key-info a{ .api-key-info a{
color:var(--accent-green); color:var(--accent-green);
text-decoration:none; text-decoration:none;
transition: color 0.3s ease; transition:color .3s ease;
} }
.api-key-info a:hover{ .api-key-info a:hover{
color:var(--accent-blue); color:var(--accent-blue);
text-decoration:underline; text-decoration:underline;
} }
/* Dashboard */
#dashboard{ #dashboard{
display:none; display:none;
animation: fadeInUp 0.6s ease-out; animation:fadeInUp .6s ease-out;
} }
.dashboard-header{ .dashboard-header{
display:flex; display:flex;
justify-content:space-between; justify-content:space-between;
@@ -319,49 +223,42 @@
flex-wrap:wrap; flex-wrap:wrap;
gap:12px; gap:12px;
} }
.account-info{ .account-info{
display:flex; display:flex;
align-items:center; align-items:center;
gap:12px; gap:12px;
} }
.status-badge{ .status-badge{
padding:6px 12px; padding:6px 12px;
border-radius:20px; border-radius:20px;
font-size:12px; font-size:12px;
font-weight:600; font-weight:600;
text-transform:uppercase; text-transform:uppercase;
letter-spacing: 0.5px; letter-spacing:.5px;
} }
.status-active{ .status-active{
background: rgba(0, 255, 136, 0.15); background:rgba(0,255,136,.15);
color:var(--accent-green); color:var(--accent-green);
border:1px solid var(--accent-green); border:1px solid var(--accent-green);
} }
.account-number{ .account-number{
font-family:'JetBrains Mono',monospace; font-family:'JetBrains Mono',monospace;
color:var(--text-secondary); color:var(--text-secondary);
font-size:14px; font-size:14px;
cursor:pointer; cursor:pointer;
transition: all 0.3s ease; transition:all .3s ease;
user-select:none; user-select:none;
position:relative; position:relative;
padding:4px 8px; padding:4px 8px;
border-radius:4px; border-radius:4px;
} }
.account-number:hover{ .account-number:hover{
color:var(--accent-green); color:var(--accent-green);
background: rgba(0, 255, 136, 0.1); background:rgba(0,255,136,.1);
} }
.account-number:active{ .account-number:active{
transform: scale(0.95); transform:scale(.95);
} }
.account-number.copied::after{ .account-number.copied::after{
content:'Copied!'; content:'Copied!';
position:absolute; position:absolute;
@@ -376,13 +273,11 @@
white-space:nowrap; white-space:nowrap;
animation:fadeOut 2s ease-out forwards; animation:fadeOut 2s ease-out forwards;
} }
@keyframes fadeOut{ @keyframes fadeOut{
0%{opacity:1;} 0%{opacity:1;}
70%{opacity:1;} 70%{opacity:1;}
100%{opacity:0;} 100%{opacity:0;}
} }
.btn-logout{ .btn-logout{
padding:8px 16px; padding:8px 16px;
background:transparent; background:transparent;
@@ -391,32 +286,27 @@
color:var(--text-secondary); color:var(--text-secondary);
font-size:14px; font-size:14px;
cursor:pointer; cursor:pointer;
transition: all 0.3s ease; transition:all .3s ease;
} }
.btn-logout:hover{ .btn-logout:hover{
border-color:var(--accent-red); border-color:var(--accent-red);
color:var(--accent-red); color:var(--accent-red);
} }
/* Stats Grid */
.stats-grid{ .stats-grid{
display:grid; display:grid;
grid-template-columns:repeat(auto-fit,minmax(250px,1fr)); grid-template-columns:repeat(auto-fit,minmax(250px,1fr));
gap:16px; gap:16px;
margin-bottom:24px; margin-bottom:24px;
} }
.stat-card{ .stat-card{
background:var(--bg-card); background:var(--bg-card);
border:1px solid var(--border); border:1px solid var(--border);
border-radius:12px; border-radius:12px;
padding:20px; padding:20px;
transition: all 0.3s ease; transition:all .3s ease;
position:relative; position:relative;
overflow:hidden; overflow:hidden;
} }
.stat-card::before{ .stat-card::before{
content:''; content:'';
position:absolute; position:absolute;
@@ -427,48 +317,39 @@
background:linear-gradient(90deg,var(--accent-green),var(--accent-blue)); background:linear-gradient(90deg,var(--accent-green),var(--accent-blue));
transform:scaleX(0); transform:scaleX(0);
transform-origin:left; transform-origin:left;
transition: transform 0.3s ease; transition:transform .3s ease;
} }
.stat-card:hover::before{ .stat-card:hover::before{
transform:scaleX(1); transform:scaleX(1);
} }
.stat-card:hover{ .stat-card:hover{
transform:translateY(-4px); transform:translateY(-4px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3); box-shadow:0 8px 24px rgba(0,0,0,.3);
} }
.stat-label{ .stat-label{
font-size:12px; font-size:12px;
color:var(--text-muted); color:var(--text-muted);
text-transform:uppercase; text-transform:uppercase;
letter-spacing: 0.5px; letter-spacing:.5px;
margin-bottom:8px; margin-bottom:8px;
} }
.stat-value{ .stat-value{
font-family:'JetBrains Mono',monospace; font-family:'JetBrains Mono',monospace;
font-size:clamp(20px,4vw,28px); font-size:clamp(20px,4vw,28px);
font-weight:700; font-weight:700;
color:var(--text-primary); color:var(--text-primary);
} }
.stat-value.positive{ .stat-value.positive{
color:var(--accent-green); color:var(--accent-green);
} }
.stat-value.negative{ .stat-value.negative{
color:var(--accent-red); color:var(--accent-red);
} }
.stat-change{ .stat-change{
font-size:13px; font-size:13px;
margin-top:4px; margin-top:4px;
font-weight:600; font-weight:600;
} }
/* Positions Table */
.section-title{ .section-title{
font-size:18px; font-size:18px;
font-weight:700; font-weight:700;
@@ -477,7 +358,6 @@
align-items:center; align-items:center;
gap:8px; gap:8px;
} }
.section-title::before{ .section-title::before{
content:''; content:'';
width:4px; width:4px;
@@ -485,7 +365,6 @@
background:var(--accent-green); background:var(--accent-green);
border-radius:2px; border-radius:2px;
} }
.positions-container{ .positions-container{
background:var(--bg-card); background:var(--bg-card);
border:1px solid var(--border); border:1px solid var(--border);
@@ -493,20 +372,16 @@
overflow:hidden; overflow:hidden;
margin-bottom:24px; margin-bottom:24px;
} }
.positions-scroll{ .positions-scroll{
overflow-x:auto; overflow-x:auto;
} }
table{ table{
width:100%; width:100%;
border-collapse:collapse; border-collapse:collapse;
} }
thead{ thead{
background:var(--bg-secondary); background:var(--bg-secondary);
} }
th{ th{
padding:16px; padding:16px;
text-align:left; text-align:left;
@@ -514,34 +389,28 @@
font-weight:600; font-weight:600;
color:var(--text-secondary); color:var(--text-secondary);
text-transform:uppercase; text-transform:uppercase;
letter-spacing: 0.5px; letter-spacing:.5px;
white-space:nowrap; white-space:nowrap;
} }
td{ td{
padding:16px; padding:16px;
border-top:1px solid var(--border); border-top:1px solid var(--border);
font-size:14px; font-size:14px;
white-space:nowrap; white-space:nowrap;
} }
.symbol{ .symbol{
font-family:'JetBrains Mono',monospace; font-family:'JetBrains Mono',monospace;
font-weight:700; font-weight:700;
color:var(--text-primary); color:var(--text-primary);
} }
.mono{ .mono{
font-family:'JetBrains Mono',monospace; font-family:'JetBrains Mono',monospace;
} }
/* Loading */
.loading{ .loading{
text-align:center; text-align:center;
padding:40px; padding:40px;
color:var(--text-secondary); color:var(--text-secondary);
} }
.spinner{ .spinner{
border:3px solid var(--border); border:3px solid var(--border);
border-top:3px solid var(--accent-green); border-top:3px solid var(--accent-green);
@@ -551,15 +420,12 @@
animation:spin 1s linear infinite; animation:spin 1s linear infinite;
margin:0 auto 16px; margin:0 auto 16px;
} }
@keyframes spin{ @keyframes spin{
0%{transform:rotate(0deg);} 0%{transform:rotate(0deg);}
100%{transform:rotate(360deg);} 100%{transform:rotate(360deg);}
} }
/* Error */
.error{ .error{
background: rgba(255, 68, 102, 0.1); background:rgba(255,68,102,.1);
border:1px solid var(--accent-red); border:1px solid var(--accent-red);
color:var(--accent-red); color:var(--accent-red);
padding:16px; padding:16px;
@@ -567,60 +433,45 @@
margin-top:16px; margin-top:16px;
font-size:14px; font-size:14px;
} }
.empty-state{ .empty-state{
text-align:center; text-align:center;
padding:60px 20px; padding:60px 20px;
color:var(--text-muted); color:var(--text-muted);
} }
.empty-state-icon{ .empty-state-icon{
font-size:48px; font-size:48px;
margin-bottom:16px; margin-bottom:16px;
opacity: 0.5; opacity:.5;
} }
/* Responsive */
@media (max-width:768px){ @media (max-width:768px){
.container{ .container{
padding:12px; padding:12px;
} }
header{ header{
padding:20px 0 20px; padding:20px 0 20px;
} }
.stats-grid{ .stats-grid{
grid-template-columns:1fr; grid-template-columns:1fr;
} }
th,td{ th,td{
padding:12px 8px; padding:12px 8px;
font-size:12px; font-size:12px;
} }
.dashboard-header{ .dashboard-header{
flex-direction:column; flex-direction:column;
align-items:flex-start; align-items:flex-start;
} }
} }
/* Refresh animation */
@keyframes pulse{ @keyframes pulse{
0%,100%{opacity:1;} 0%,100%{opacity:1;}
50% { opacity: 0.5; } 50%{opacity:.5;}
} }
.refreshing{ .refreshing{
animation:pulse 1.5s ease-in-out infinite; animation:pulse 1.5s ease-in-out infinite;
} }
</style> </style>
</head> </head>
<body> <body>
<div class="bg-grid"></div>
<div class="orb orb-1"></div>
<div class="orb orb-2"></div>
<div class="container"> <div class="container">
<header> <header>
<div class="logo-container"> <div class="logo-container">
@@ -630,11 +481,9 @@
<div class="tagline">Real-time portfolio monitoring</div> <div class="tagline">Real-time portfolio monitoring</div>
</header> </header>
<!-- Login Form -->
<div id="loginForm"> <div id="loginForm">
<div class="login-card"> <div class="login-card">
<form id="loginFormElement" onsubmit="login(event)"> <form id="loginFormElement" onsubmit="login(event)">
<!-- Trading Mode Selection -->
<div class="trading-mode-group"> <div class="trading-mode-group">
<div class="mode-options"> <div class="mode-options">
<div class="mode-option"> <div class="mode-option">
@@ -654,26 +503,13 @@
</div> </div>
</div> </div>
<!-- API Credentials -->
<div class="input-group"> <div class="input-group">
<label for="apiKey">API Key</label> <label for="apiKey">API Key</label>
<input <input type="text" id="apiKey" name="username" placeholder="PK••••••••••••••••" autocomplete="username">
type="text"
id="apiKey"
name="username"
placeholder="PK••••••••••••••••"
autocomplete="username"
>
</div> </div>
<div class="input-group"> <div class="input-group">
<label for="apiSecret">API Secret</label> <label for="apiSecret">API Secret</label>
<input <input type="password" id="apiSecret" name="password" placeholder="••••••••••••••••" autocomplete="current-password">
type="password"
id="apiSecret"
name="password"
placeholder="••••••••••••••••"
autocomplete="current-password"
>
</div> </div>
<button type="submit" class="btn-go">GO!</button> <button type="submit" class="btn-go">GO!</button>
<div class="api-key-info"> <div class="api-key-info">
@@ -684,23 +520,16 @@
</div> </div>
</div> </div>
<!-- Dashboard -->
<div id="dashboard"> <div id="dashboard">
<div class="dashboard-header"> <div class="dashboard-header">
<div class="account-info"> <div class="account-info">
<span class="status-badge status-active">● LIVE</span> <span class="status-badge status-active">● LIVE</span>
<span id="accountNumber" class="account-number" <span id="accountNumber" class="account-number" onmouseenter="revealAccountNumber()" onmouseleave="hideAccountNumber()" onclick="copyAccountNumber(event)" oncontextmenu="copyAccountNumber(event)">#PA••••••••</span>
onmouseenter="revealAccountNumber()"
onmouseleave="hideAccountNumber()"
onclick="copyAccountNumber(event)"
oncontextmenu="copyAccountNumber(event)">#PA••••••••</span>
</div> </div>
<button class="btn-logout" onclick="logout()">Logout</button> <button class="btn-logout" onclick="logout()">Logout</button>
</div> </div>
<div class="stats-grid" id="statsGrid"> <div class="stats-grid" id="statsGrid"></div>
<!-- Stats will be inserted here -->
</div>
<h2 class="section-title">Positions</h2> <h2 class="section-title">Positions</h2>
<div class="positions-container"> <div class="positions-container">
@@ -713,13 +542,11 @@
<th>Avg Cost</th> <th>Avg Cost</th>
<th>Current Price</th> <th>Current Price</th>
<th>Market Value</th> <th>Market Value</th>
<th>P&L</th> <th>P&amp;L</th>
<th>P&L %</th> <th>P&amp;L %</th>
</tr> </tr>
</thead> </thead>
<tbody id="positionsBody"> <tbody id="positionsBody"></tbody>
<!-- Positions will be inserted here -->
</tbody>
</table> </table>
</div> </div>
</div> </div>
@@ -729,14 +556,13 @@
<script> <script>
let API_KEY = ''; let API_KEY = '';
let API_SECRET = ''; let API_SECRET = '';
let BASE_URL = 'https://paper-api.alpaca.markets'; // Default to paper trading let BASE_URL = 'https://paper-api.alpaca.markets';
let ACCOUNT_NUMBER = ''; let ACCOUNT_NUMBER = '';
let hideTimeout = null; let hideTimeout = null;
let isRevealed = false; let isRevealed = false;
async function login(event) { async function login(event) {
event.preventDefault(); // Prevent form submission event.preventDefault();
const apiKey = document.getElementById('apiKey').value.trim(); const apiKey = document.getElementById('apiKey').value.trim();
const apiSecret = document.getElementById('apiSecret').value.trim(); const apiSecret = document.getElementById('apiSecret').value.trim();
const errorDiv = document.getElementById('loginError'); const errorDiv = document.getElementById('loginError');
@@ -749,8 +575,6 @@
API_KEY = apiKey; API_KEY = apiKey;
API_SECRET = apiSecret; API_SECRET = apiSecret;
// Set BASE_URL based on selected trading mode
BASE_URL = tradingMode === 'paper' BASE_URL = tradingMode === 'paper'
? 'https://paper-api.alpaca.markets' ? 'https://paper-api.alpaca.markets'
: 'https://api.alpaca.markets'; : 'https://api.alpaca.markets';
@@ -767,7 +591,6 @@
document.getElementById('loginForm').style.display = 'none'; document.getElementById('loginForm').style.display = 'none';
document.getElementById('dashboard').style.display = 'block'; document.getElementById('dashboard').style.display = 'block';
await loadDashboard(); await loadDashboard();
// Auto-refresh every 30 seconds
setInterval(loadDashboard, 30000); setInterval(loadDashboard, 30000);
return; return;
} }
@@ -809,7 +632,6 @@
function displayAccount(account) { function displayAccount(account) {
ACCOUNT_NUMBER = account.account_number; ACCOUNT_NUMBER = account.account_number;
// Set initial display with first 3 characters visible
updateAccountDisplay(); updateAccountDisplay();
const equity = parseFloat(account.equity); const equity = parseFloat(account.equity);
@@ -819,27 +641,10 @@
const plTodayPercent = (plToday / parseFloat(account.last_equity)) * 100; const plTodayPercent = (plToday / parseFloat(account.last_equity)) * 100;
const stats = [ const stats = [
{ { label: 'Portfolio Value', value: formatCurrency(equity), class: '' },
label: 'Portfolio Value', { label: 'Cash', value: formatCurrency(cash), class: '' },
value: formatCurrency(equity), { label: 'Buying Power', value: formatCurrency(buyingPower), class: '' },
class: '' { label: "Today's P&L", value: formatCurrency(plToday), change: `${plToday >= 0 ? '+' : ''}${plTodayPercent.toFixed(2)}%`, class: plToday >= 0 ? 'positive' : 'negative' }
},
{
label: 'Cash',
value: formatCurrency(cash),
class: ''
},
{
label: 'Buying Power',
value: formatCurrency(buyingPower),
class: ''
},
{
label: 'Today\'s P&L',
value: formatCurrency(plToday),
change: `${plTodayPercent >= 0 ? '+' : ''}${plTodayPercent.toFixed(2)}%`,
class: plToday >= 0 ? 'positive' : 'negative'
}
]; ];
const statsGrid = document.getElementById('statsGrid'); const statsGrid = document.getElementById('statsGrid');
@@ -853,8 +658,7 @@
} }
function getPartialAccountNumber() { function getPartialAccountNumber() {
// Show first 3 characters after # and mask the rest const prefix = ACCOUNT_NUMBER.substring(0, 2);
const prefix = ACCOUNT_NUMBER.substring(0, 2); // PA or similar
const dots = '•'.repeat(ACCOUNT_NUMBER.length - 2); const dots = '•'.repeat(ACCOUNT_NUMBER.length - 2);
return `#${prefix}${dots}`; return `#${prefix}${dots}`;
} }
@@ -869,18 +673,15 @@
} }
function revealAccountNumber() { function revealAccountNumber() {
// Clear any pending hide timeout
if (hideTimeout) { if (hideTimeout) {
clearTimeout(hideTimeout); clearTimeout(hideTimeout);
hideTimeout = null; hideTimeout = null;
} }
isRevealed = true; isRevealed = true;
updateAccountDisplay(); updateAccountDisplay();
} }
function hideAccountNumber() { function hideAccountNumber() {
// Only hide if not clicked (click sets a 5 second timer)
if (!hideTimeout) { if (!hideTimeout) {
isRevealed = false; isRevealed = false;
updateAccountDisplay(); updateAccountDisplay();
@@ -890,30 +691,19 @@
function copyAccountNumber(event) { function copyAccountNumber(event) {
event.preventDefault(); event.preventDefault();
const accountElement = document.getElementById('accountNumber'); const accountElement = document.getElementById('accountNumber');
// Reveal the account number
isRevealed = true; isRevealed = true;
updateAccountDisplay(); updateAccountDisplay();
// Clear any existing timeout
if (hideTimeout) { if (hideTimeout) {
clearTimeout(hideTimeout); clearTimeout(hideTimeout);
} }
// Copy to clipboard
navigator.clipboard.writeText(`#${ACCOUNT_NUMBER}`).then(() => { navigator.clipboard.writeText(`#${ACCOUNT_NUMBER}`).then(() => {
// Add copied class for visual feedback
accountElement.classList.add('copied'); accountElement.classList.add('copied');
// Remove the copied class after animation completes
setTimeout(() => { setTimeout(() => {
accountElement.classList.remove('copied'); accountElement.classList.remove('copied');
}, 2000); }, 2000);
}).catch(err => { }).catch(err => {
console.error('Failed to copy:', err); console.error('Failed to copy:', err);
}); });
// Set a timeout to hide after 5 seconds
hideTimeout = setTimeout(() => { hideTimeout = setTimeout(() => {
isRevealed = false; isRevealed = false;
updateAccountDisplay(); updateAccountDisplay();
@@ -942,7 +732,6 @@
const pl = parseFloat(pos.unrealized_pl); const pl = parseFloat(pos.unrealized_pl);
const plPercent = parseFloat(pos.unrealized_plpc) * 100; const plPercent = parseFloat(pos.unrealized_plpc) * 100;
const plClass = pl >= 0 ? 'positive' : 'negative'; const plClass = pl >= 0 ? 'positive' : 'negative';
return ` return `
<tr> <tr>
<td class="symbol">${pos.symbol}</td> <td class="symbol">${pos.symbol}</td>