From 84c2a99456653373e119c5b5f48afa36b644450d Mon Sep 17 00:00:00 2001 From: KS Jannette Date: Sun, 22 Feb 2026 17:10:24 -0500 Subject: [PATCH 1/3] uodated charting --- .gitignore | 1 + trahn-trade-frontend/index.html | 2 - trahn-trade-frontend/src/App.css | 763 +++++++++--------- trahn-trade-frontend/src/api/tradingApi.js | 2 +- trahn-trade-frontend/src/components/Chart.jsx | 644 ++++++++------- .../src/fonts/Ac437_Rainbow100_re_132.ttf | Bin 0 -> 24700 bytes .../src/fonts/Mx437_Compaq_Port3.ttf | Bin 0 -> 50188 bytes .../src/fonts/Px437_AMI_EGA_8x8.ttf | Bin 0 -> 23748 bytes .../src/fonts/Px437_Robotron_A7100.ttf | Bin 0 -> 27384 bytes trahn-trade-frontend/src/index.css | 35 +- trahn-trade-frontend/src/pages/Dashboard.jsx | 8 +- trahn-trade-frontend/src/pages/Login.jsx | 8 +- trahn-trade-frontend/src/pages/Signup.jsx | 8 +- 13 files changed, 756 insertions(+), 715 deletions(-) create mode 100644 trahn-trade-frontend/src/fonts/Ac437_Rainbow100_re_132.ttf create mode 100644 trahn-trade-frontend/src/fonts/Mx437_Compaq_Port3.ttf create mode 100644 trahn-trade-frontend/src/fonts/Px437_AMI_EGA_8x8.ttf create mode 100644 trahn-trade-frontend/src/fonts/Px437_Robotron_A7100.ttf diff --git a/.gitignore b/.gitignore index 724549c..68a9599 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.key *.env *secrets.js +secrets.js trahn-trade-frontend/src/auth/secrets.js* # ============================================ diff --git a/trahn-trade-frontend/index.html b/trahn-trade-frontend/index.html index 8bf5084..79b13ce 100644 --- a/trahn-trade-frontend/index.html +++ b/trahn-trade-frontend/index.html @@ -5,8 +5,6 @@ TRAHN Grid Trader - -
diff --git a/trahn-trade-frontend/src/App.css b/trahn-trade-frontend/src/App.css index 3fff0d5..7b6075e 100644 --- a/trahn-trade-frontend/src/App.css +++ b/trahn-trade-frontend/src/App.css @@ -1,612 +1,649 @@ :root { - --bg-primary: #0d1117; - --bg-secondary: #161b22; - --bg-tertiary: #21262d; - --text-primary: #e6edf3; - --text-secondary: #8b949e; - --text-muted: #484f58; - --accent: #58a6ff; - --green: #3fb950; - --green-fill: rgba(63, 185, 80, 0.15); - --red: #f85149; - --red-fill: rgba(248, 81, 73, 0.15); - --yellow: #f0c000; - --orange: #db6d28; - --border: #30363d; + --bg-primary: #000000; + --bg-secondary: #070808; + --bg-tertiary: #363737; + --text-primary: #01ff3c; + --text-secondary: #01ff3c; + --text-muted: #484f58; + --accent: #58a6ff; + --green: #3fb950; + --green-fill: rgba(63, 185, 80, 0.15); + --red: #f85149; + --red-fill: rgba(248, 81, 73, 0.15); + --yellow: #f0c000; + --orange: #db6d28; + --border: #30363d; } * { - margin: 0; - padding: 0; - box-sizing: border-box; + margin: 0; + padding: 0; + box-sizing: border-box; } body { - font-family: 'Space Grotesk', -apple-system, BlinkMacSystemFont, sans-serif; - background: var(--bg-primary); - color: var(--text-primary); - min-height: 100vh; - padding: 2rem; + font-family: 'Ac437_Rainbow100_re_132', monospace; + font-size: 18px; + background: var(--bg-primary); + color: var(--text-primary); + min-height: 100vh; + padding: 2rem; } .container { - max-width: 1200px; - margin: 0 auto; + max-width: 1200px; + margin: 0 auto; } header { - text-align: center; - margin-bottom: 2rem; + display: flex; + flex-direction: column; + text-align: center; + margin-right: auto; + margin-left: auto; + margin-bottom: 2rem; } -.ascii-logo { - font-family: 'JetBrains Mono', monospace; - font-size: 0.65rem; - line-height: 1.1; - color: var(--accent); - margin: 0; - display: inline-block; +.logo { + margin-left: 32px; + font-family: 'Ac437_Rainbow100_re_132', monospace; + font-size: 9rem; + font-weight: 700; + color: var(--text-primary); + text-align: center; + letter-spacing: 0.25em; + text-shadow: 0 10 30px rgba(88, 166, 255, 0.3); } .subtitle { - font-family: 'JetBrains Mono', monospace; - font-size: 0.875rem; - color: var(--text-secondary); - letter-spacing: 0.05em; + font-family: 'Ac437_Rainbow100_re_132', monospace; + font-size: 0.875rem; + color: var(--text-secondary); + letter-spacing: 0.05em; } /* Carousel Navigation */ .carousel-nav { - display: flex; - align-items: center; - justify-content: center; - gap: 1.5rem; - margin-bottom: 1rem; + display: flex; + align-items: center; + justify-content: center; + gap: 1.5rem; + margin-bottom: 1rem; } .nav-arrow { - background: var(--bg-secondary); - border: 1px solid var(--border); - color: var(--text-primary); - width: 40px; - height: 40px; - border-radius: 8px; - cursor: pointer; - font-size: 1rem; - transition: all 0.2s ease; - display: flex; - align-items: center; - justify-content: center; + background: var(--bg-secondary); + border: 1px solid var(--border); + color: var(--text-primary); + width: 40px; + height: 40px; + border-radius: 8px; + cursor: pointer; + font-size: 1rem; + transition: all 0.2s ease; + display: flex; + align-items: center; + justify-content: center; } .nav-arrow:hover:not(:disabled) { - background: var(--bg-tertiary); - border-color: var(--accent); - color: var(--accent); + background: var(--bg-tertiary); + border-color: var(--accent); + color: var(--accent); } .nav-arrow:disabled { - opacity: 0.3; - cursor: not-allowed; + opacity: 0.3; + cursor: not-allowed; } .date-display { - text-align: center; - min-width: 200px; + text-align: center; + min-width: 200px; } .date-label { - display: block; - font-size: 1.25rem; - font-weight: 600; - color: var(--text-primary); + font-family: 'Ac437_Rainbow100_re_132'; + font-weight: 600; + font-size: 1.45rem; + font-weight: 600; + color: var(--text-primary); } .date-status { - display: block; - font-family: 'JetBrains Mono', monospace; - font-size: 0.75rem; - color: var(--text-secondary); - margin-top: 0.25rem; + display: block; + font-family: 'Ac437_Rainbow100_re_132', monospace; + font-size: 0.75rem; + color: var(--text-secondary); + margin-top: 0.25rem; } .date-status.live { - color: var(--green); + color: var(--green); } .date-status.live::before { - content: '●'; - margin-right: 0.5rem; - animation: pulse 1.5s ease-in-out infinite; + content: '●'; + margin-right: 0.5rem; + animation: pulse 1.5s ease-in-out infinite; } @keyframes pulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.4; } + + 0%, + 100% { + opacity: 1; + } + + 50% { + opacity: 0.4; + } } /* Chart */ .chart-wrapper { - position: relative; - margin-bottom: 1.5rem; + position: relative; + margin-bottom: 1.5rem; } .chart-container { - background: var(--bg-secondary); - border: 1px solid var(--border); - border-radius: 12px; - padding: 1.5rem; - height: 400px; + background: var(--bg-secondary); + border: 1px solid var(--border); + border-radius: 12px; + padding: 1.5rem; + height: 400px; } .tooltip { - position: absolute; - background: var(--bg-tertiary); - border: 1px solid var(--border); - border-radius: 6px; - padding: 0.5rem 0.75rem; - font-family: 'JetBrains Mono', monospace; - font-size: 0.75rem; - pointer-events: none; - opacity: 0; - transition: opacity 0.15s ease; - z-index: 100; - white-space: nowrap; + position: absolute; + background: var(--bg-tertiary); + border: 1px solid var(--border); + border-radius: 6px; + padding: 0.5rem 0.75rem; + font-family: 'Ac437_Rainbow100_re_132', monospace; + font-size: 0.75rem; + pointer-events: none; + opacity: 0; + transition: opacity 0.15s ease; + z-index: 100; + white-space: nowrap; } .tooltip.visible { - opacity: 1; + opacity: 1; } .tooltip .price { - font-size: 0.875rem; - font-weight: 600; - color: var(--text-primary); + font-size: 0.875rem; + font-weight: 600; + color: var(--text-primary); } .tooltip .time { - color: var(--text-secondary); - margin-top: 2px; + color: var(--text-secondary); + margin-top: 2px; } /* Loading overlay */ .loading-overlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: var(--bg-secondary); - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 1rem; - border-radius: 12px; - color: var(--text-secondary); - font-family: 'JetBrains Mono', monospace; - z-index: 50; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: var(--bg-secondary); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 1rem; + border-radius: 12px; + color: var(--text-secondary); + font-family: 'Ac437_Rainbow100_re_132', monospace; + z-index: 50; } .spinner { - width: 30px; - height: 30px; - border: 3px solid var(--border); - border-top-color: var(--accent); - border-radius: 50%; - animation: spin 1s linear infinite; + width: 30px; + height: 30px; + border: 3px solid var(--border); + border-top-color: var(--accent); + border-radius: 50%; + animation: spin 1s linear infinite; } @keyframes spin { - to { transform: rotate(360deg); } + to { + transform: rotate(360deg); + } } /* Stats */ .stats { - display: flex; - justify-content: space-between; - gap: 1rem; - margin-bottom: 1.5rem; + display: flex; + font-size: 1.2rem; + justify-content: space-between; + gap: 1rem; + margin-bottom: 1.5rem; } .stat { - flex: 1; - background: var(--bg-secondary); - border: 1px solid var(--border); - border-radius: 8px; - padding: 1rem; - text-align: center; + flex: 1; + font-size: 1.2rem; + background: var(--bg-secondary); + border: 1px solid var(--border); + border-radius: 8px; + padding: 1rem; + text-align: center; } .stat-label { - display: block; - font-size: 0.75rem; - color: var(--text-secondary); - text-transform: uppercase; - letter-spacing: 0.05em; - margin-bottom: 0.25rem; + display: block; + font-size: 0.75rem; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 0.25rem; } .stat-value { - font-family: 'JetBrains Mono', monospace; - font-size: 1.25rem; - font-weight: 600; + font-family: 'Ac437_Rainbow100_re_132', monospace; + font-size: 1.25rem; + font-weight: 600; } -.stat-value.high { color: var(--green); } -.stat-value.low { color: var(--red); } -.stat-value.buy { color: var(--yellow); } -.stat-value.sell { color: var(--orange); } +.stat-value.high { + color: var(--green); +} + +.stat-value.low { + color: var(--red); +} + +.stat-value.buy { + color: var(--yellow); +} + +.stat-value.sell { + color: var(--orange); +} /* Day indicators */ .day-indicators { - display: flex; - justify-content: center; - gap: 0.5rem; - margin-bottom: 1rem; + display: flex; + justify-content: center; + gap: 0.5rem; + margin-bottom: 1rem; } .day-dot { - width: 8px; - height: 8px; - border-radius: 50%; - background: var(--border); - cursor: pointer; - transition: all 0.2s ease; + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--border); + cursor: pointer; + transition: all 0.2s ease; } .day-dot:hover { - background: var(--text-secondary); + background: var(--text-secondary); } .day-dot.active { - background: var(--accent); - box-shadow: 0 0 8px var(--accent); + background: var(--accent); + box-shadow: 0 0 8px var(--accent); } /* Legend */ .legend { - display: flex; - justify-content: center; - gap: 2rem; - font-size: 0.875rem; - color: var(--text-secondary); + display: flex; + justify-content: center; + gap: 2rem; + font-size: 0.875rem; + color: var(--text-secondary); } .legend-item { - display: flex; - align-items: center; - gap: 0.5rem; + display: flex; + align-items: center; + font-size: 1rem; + gap: 0.5rem; } .dot { - width: 10px; - height: 10px; - border-radius: 50%; + width: 10px; + height: 10px; + border-radius: 50%; } .buy-dot { - background: var(--yellow); - box-shadow: 0 0 8px var(--yellow); + background: var(--yellow); + box-shadow: 0 0 8px var(--yellow); } .sell-dot { - background: var(--orange); - box-shadow: 0 0 8px var(--orange); + background: var(--orange); + box-shadow: 0 0 8px var(--orange); } /* Connection status */ .connection-status { - position: fixed; - bottom: 1rem; - right: 1rem; - display: flex; - align-items: center; - gap: 0.5rem; - background: var(--bg-secondary); - border: 1px solid var(--border); - border-radius: 6px; - padding: 0.5rem 0.75rem; - font-family: 'JetBrains Mono', monospace; - font-size: 0.75rem; - color: var(--text-secondary); + position: fixed; + bottom: 1rem; + right: 1rem; + display: flex; + align-items: center; + gap: 0.5rem; + background: var(--bg-secondary); + border: 1px solid var(--border); + border-radius: 6px; + padding: 0.5rem 0.75rem; + font-family: 'Ac437_Rainbow100_re_132', monospace; + font-size: 0.75rem; + color: var(--text-secondary); } .connection-status .status-dot { - width: 8px; - height: 8px; - border-radius: 50%; - background: var(--text-muted); + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--text-muted); } .connection-status.connected .status-dot { - background: var(--green); - box-shadow: 0 0 6px var(--green); + background: var(--green); + box-shadow: 0 0 6px var(--green); } .connection-status.waiting .status-dot { - background: var(--yellow); - animation: pulse 1.5s ease-in-out infinite; + background: var(--yellow); + animation: pulse 1.5s ease-in-out infinite; } /* Responsive */ @media (max-width: 768px) { - body { - padding: 1rem; - } - - h1 { - font-size: 1.75rem; - } - - .stats { - flex-wrap: wrap; - } - - .stat { - flex: 1 1 45%; - } - - .chart-container { - height: 300px; - } + body { + padding: 1rem; + } + + h1 { + font-size: 1.75rem; + } + + .stats { + flex-wrap: wrap; + } + + .stat { + flex: 1 1 45%; + } + + .chart-container { + height: 300px; + } } /* Grid Levels Display */ .grid-levels { - margin-top: 1.5rem; - padding: 1rem; - background: var(--bg-secondary); - border: 1px solid var(--border); - border-radius: 8px; + margin-top: 1.5rem; + padding: 1rem; + background: var(--bg-secondary); + border: 1px solid var(--border); + border-radius: 8px; } .grid-levels-header { - text-align: center; - font-size: 0.875rem; - font-weight: 600; - color: var(--text-secondary); - margin-bottom: 1rem; - text-transform: uppercase; - letter-spacing: 0.05em; + text-align: center; + font-size: 1.2rem; + font-weight: 600; + color: var(--text-secondary); + margin-bottom: 1rem; + text-transform: uppercase; + letter-spacing: 0.05em; } .grid-levels-container { - display: flex; - justify-content: center; - gap: 2rem; + display: flex; + justify-content: center; + gap: 2rem; } .grid-column { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.375rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 0.375rem; } .grid-column-header { - font-size: 0.75rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.05em; - margin-bottom: 0.5rem; - padding: 0.25rem 0.75rem; - border-radius: 4px; + font-size: 1.2rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 0.5rem; + padding: 0.25rem 0.75rem; + border-radius: 4px; } .grid-column-header.buy { - color: var(--yellow); - background: rgba(240, 192, 0, 0.1); + font-size: 1.2rem; + color: var(--yellow); + background: rgba(240, 192, 0, 0.1); } .grid-column-header.sell { - color: var(--orange); - background: rgba(219, 109, 40, 0.1); + font-size: 1.2rem; + color: var(--orange); + background: rgba(219, 109, 40, 0.1); } .grid-level { - font-family: 'JetBrains Mono', monospace; - font-size: 0.8rem; - padding: 0.25rem 0.75rem; - border-radius: 4px; - min-width: 100px; - text-align: center; + font-family: 'Ac437_Rainbow100_re_132', monospace; + font-size: 1.2rem; + padding: 0.25rem 0.75rem; + border-radius: 4px; + min-width: 100px; + text-align: center; } .grid-level.buy { - color: var(--yellow); - background: rgba(240, 192, 0, 0.08); - border: 1px solid rgba(240, 192, 0, 0.2); + color: var(--yellow); + font-size: 1.2rem; + background: rgba(240, 192, 0, 0.08); + border: 1px solid rgba(240, 192, 0, 0.2); } .grid-level.sell { - color: var(--orange); - background: rgba(219, 109, 40, 0.08); - border: 1px solid rgba(219, 109, 40, 0.2); + color: var(--orange); + background: rgba(219, 109, 40, 0.08); + border: 1px solid rgba(219, 109, 40, 0.2); } .grid-level.filled { - opacity: 0.5; - text-decoration: line-through; + opacity: 0.5; + text-decoration: line-through; } /* Auth Pages */ .auth-container { - min-height: 100vh; - display: flex; - align-items: center; - justify-content: center; - padding: 2rem; + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + padding: 2rem; } .auth-card { - background: var(--bg-secondary); - border: 1px solid var(--border); - border-radius: 12px; - padding: 2.5rem; - width: 100%; - max-width: 420px; - text-align: center; + background: var(--bg-secondary); + border: 1px solid var(--border); + border-radius: 12px; + padding: 2.5rem; + width: 100%; + max-width: 420px; + text-align: center; } -.auth-card .ascii-logo { - margin-bottom: 1.5rem; +.auth-card .logo { + margin-bottom: 1.5rem; } .auth-card h2 { - font-size: 1.5rem; - font-weight: 600; - color: var(--text-primary); - margin-bottom: 1.5rem; + font-size: 1.5rem; + font-weight: 600; + color: var(--text-primary); + margin-bottom: 1.5rem; } .auth-form { - display: flex; - flex-direction: column; - gap: 1rem; + display: flex; + flex-direction: column; + gap: 1rem; } .form-group { - text-align: left; + text-align: left; } .form-group label { - display: block; - font-size: 0.875rem; - color: var(--text-secondary); - margin-bottom: 0.5rem; + display: block; + font-size: 0.875rem; + color: var(--text-secondary); + margin-bottom: 0.5rem; } .form-group input { - width: 100%; - padding: 0.75rem 1rem; - background: var(--bg-primary); - border: 1px solid var(--border); - border-radius: 8px; - color: var(--text-primary); - font-size: 1rem; - font-family: inherit; - transition: border-color 0.2s, box-shadow 0.2s; + width: 100%; + padding: 0.75rem 1rem; + background: var(--bg-primary); + border: 1px solid var(--border); + border-radius: 8px; + color: var(--text-primary); + font-size: 1rem; + font-family: inherit; + transition: border-color 0.2s, box-shadow 0.2s; } .form-group input:focus { - outline: none; - border-color: var(--accent); - box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.15); + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.15); } .form-group input::placeholder { - color: var(--text-muted); + color: var(--text-muted); } .auth-btn { - width: 100%; - padding: 0.875rem 1.5rem; - background: var(--accent); - color: var(--bg-primary); - border: none; - border-radius: 8px; - font-size: 1rem; - font-weight: 600; - font-family: inherit; - cursor: pointer; - transition: opacity 0.2s, transform 0.1s; - margin-top: 0.5rem; + width: 100%; + padding: 0.875rem 1.5rem; + background: var(--accent); + color: var(--bg-primary); + border: none; + border-radius: 8px; + font-size: 1rem; + font-weight: 600; + font-family: inherit; + cursor: pointer; + transition: opacity 0.2s, transform 0.1s; + margin-top: 0.5rem; } .auth-btn:hover:not(:disabled) { - opacity: 0.9; + opacity: 0.9; } .auth-btn:active:not(:disabled) { - transform: scale(0.98); + transform: scale(0.98); } .auth-btn:disabled { - opacity: 0.6; - cursor: not-allowed; + opacity: 0.6; + cursor: not-allowed; } .auth-error { - background: var(--red-fill); - border: 1px solid var(--red); - color: var(--red); - padding: 0.75rem 1rem; - border-radius: 8px; - font-size: 0.875rem; - margin-bottom: 1rem; + background: var(--red-fill); + border: 1px solid var(--red); + color: var(--red); + padding: 0.75rem 1rem; + border-radius: 8px; + font-size: 0.875rem; + margin-bottom: 1rem; } .auth-success { - background: var(--green-fill); - border: 1px solid var(--green); - color: var(--green); - padding: 0.75rem 1rem; - border-radius: 8px; - font-size: 0.875rem; - margin-bottom: 1rem; + background: var(--green-fill); + border: 1px solid var(--green); + color: var(--green); + padding: 0.75rem 1rem; + border-radius: 8px; + font-size: 0.875rem; + margin-bottom: 1rem; } .auth-link { - margin-top: 1.5rem; - color: var(--text-secondary); - font-size: 0.875rem; + margin-top: 1.5rem; + color: var(--text-secondary); + font-size: 0.875rem; } .auth-link a { - color: var(--accent); - text-decoration: none; - font-weight: 600; + color: var(--accent); + text-decoration: none; + font-weight: 600; } .auth-link a:hover { - text-decoration: underline; + text-decoration: underline; } /* Waitlist Message */ .waitlist-message { - background: var(--bg-tertiary); - border: 1px solid var(--border); - border-radius: 8px; - padding: 1.5rem; - margin: 1rem 0; - text-align: left; - line-height: 1.6; + background: var(--bg-tertiary); + border: 1px solid var(--border); + border-radius: 8px; + padding: 1.5rem; + margin: 1rem 0; + text-align: left; + line-height: 1.6; } .waitlist-message p { - color: var(--text-secondary); - margin-bottom: 1rem; + color: var(--text-secondary); + margin-bottom: 1rem; } .waitlist-message .thank-you { - color: var(--accent); - font-weight: 600; - text-align: center; - margin-bottom: 0; + color: var(--accent); + font-weight: 600; + text-align: center; + margin-bottom: 0; } /* Logout Button */ .logout-btn { - position: absolute; - top: 1rem; - right: 1rem; - padding: 0.5rem 1rem; - background: transparent; - border: 1px solid var(--border); - border-radius: 6px; - color: var(--text-secondary); - font-size: 0.875rem; - font-family: inherit; - cursor: pointer; - transition: border-color 0.2s, color 0.2s; + height: 40px; + width: 110px; + font-size: 1.2rem; + font-family: Ac437_Rainbow100_re_132; + position: absolute; + top: 1rem; + right: 1rem; + padding: 0.5rem 1rem; + background: transparent; + border: 1px solid var(--border); + border-radius: 6px; + color: var(--text-secondary); + font-family: inherit; + cursor: pointer; + transition: border-color 0.2s, color 0.2s; } .logout-btn:hover { - border-color: var(--red); - color: var(--red); + border-color: var(--red); + color: var(--red); } header { - position: relative; -} + position: relative; +} \ No newline at end of file diff --git a/trahn-trade-frontend/src/api/tradingApi.js b/trahn-trade-frontend/src/api/tradingApi.js index f1d1ba9..b3fbc6a 100644 --- a/trahn-trade-frontend/src/api/tradingApi.js +++ b/trahn-trade-frontend/src/api/tradingApi.js @@ -3,7 +3,7 @@ * Communicates with backend REST API */ -const API_BASE = 'http://localhost:3001/api'; +const API_BASE = 'http://localhost:3001/v1'; export const tradingApi = { /** diff --git a/trahn-trade-frontend/src/components/Chart.jsx b/trahn-trade-frontend/src/components/Chart.jsx index 649fa97..3da40a6 100644 --- a/trahn-trade-frontend/src/components/Chart.jsx +++ b/trahn-trade-frontend/src/components/Chart.jsx @@ -1,350 +1,340 @@ import { useRef, useEffect, useState } from 'react'; const COLORS = { - line: '#58a6ff', - green: '#3fb950', - greenFill: 'rgba(63, 185, 80, 0.12)', - red: '#f85149', - redFill: 'rgba(248, 81, 73, 0.12)', - grid: '#21262d', - text: '#8b949e', - textMuted: '#484f58', - baseline: '#30363d', - buy: '#f0c000', - sell: '#db6d28', - bg: '#0d1117', + line: '#58a6ff', + green: '#3fb950', + greenFill: 'rgba(63, 185, 80, 0.12)', + red: '#f85149', + redFill: 'rgba(248, 81, 73, 0.12)', + grid: '#21262d', + text: '#8b949e', + textMuted: '#484f58', + baseline: '#30363d', + buy: '#f0c000', + sell: '#db6d28', + bg: '#0d1117', }; const PADDING = { top: 20, right: 80, bottom: 40, left: 20 }; export function Chart({ prices, trades }) { - const canvasRef = useRef(null); - const containerRef = useRef(null); - const [tooltip, setTooltip] = useState(null); - const [dimensions, setDimensions] = useState({ width: 0, height: 0 }); + const canvasRef = useRef(null); + const containerRef = useRef(null); + const [tooltip, setTooltip] = useState(null); + const [dimensions, setDimensions] = useState({ width: 0, height: 0 }); - // Handle resize - useEffect(() => { - const updateDimensions = () => { - if (containerRef.current) { - const rect = containerRef.current.getBoundingClientRect(); - setDimensions({ width: rect.width, height: rect.height }); - } - }; - - updateDimensions(); - window.addEventListener('resize', updateDimensions); - return () => window.removeEventListener('resize', updateDimensions); - }, []); - - // Draw chart - useEffect(() => { - const canvas = canvasRef.current; - if (!canvas || dimensions.width === 0) return; - - const ctx = canvas.getContext('2d'); - const dpr = window.devicePixelRatio || 1; - - canvas.width = dimensions.width * dpr; - canvas.height = dimensions.height * dpr; - ctx.scale(dpr, dpr); - - const width = dimensions.width; - const height = dimensions.height; - const chartWidth = width - PADDING.left - PADDING.right; - const chartHeight = height - PADDING.top - PADDING.bottom; - - // Clear - ctx.clearRect(0, 0, width, height); - - if (!prices || prices.length === 0) { - ctx.fillStyle = COLORS.textMuted; - ctx.font = '14px JetBrains Mono, monospace'; - ctx.textAlign = 'center'; - ctx.fillText('No data for this day', width / 2, height / 2); - return; - } - - // Calculate bounds - const priceValues = prices.map(d => d.p || d.price); - const times = prices.map(d => d.t || d.timestamp); - - let minPrice = Math.min(...priceValues); - let maxPrice = Math.max(...priceValues); - const minTime = Math.min(...times); - const maxTime = Math.max(...times); - - const pricePadding = (maxPrice - minPrice) * 0.05 || 10; - minPrice -= pricePadding; - maxPrice += pricePadding; - - const baselinePrice = priceValues[0]; - - // Coordinate converters - const priceToY = (price) => { - const range = maxPrice - minPrice; - const normalized = (price - minPrice) / range; - return PADDING.top + chartHeight * (1 - normalized); - }; - - const timeToX = (timestamp) => { - const range = maxTime - minTime; - const normalized = (timestamp - minTime) / range; - return PADDING.left + chartWidth * normalized; - }; - - // Draw grid - ctx.strokeStyle = COLORS.grid; - ctx.lineWidth = 1; - - const priceStep = calculateNiceStep(maxPrice - minPrice, 5); - const startPrice = Math.ceil(minPrice / priceStep) * priceStep; - - for (let price = startPrice; price <= maxPrice; price += priceStep) { - const y = priceToY(price); - ctx.beginPath(); - ctx.moveTo(PADDING.left, y); - ctx.lineTo(width - PADDING.right, y); - ctx.stroke(); - } - - // Draw baseline - const baselineY = priceToY(baselinePrice); - ctx.strokeStyle = COLORS.baseline; - ctx.setLineDash([4, 4]); - ctx.beginPath(); - ctx.moveTo(PADDING.left, baselineY); - ctx.lineTo(width - PADDING.right, baselineY); - ctx.stroke(); - ctx.setLineDash([]); - - // Draw gradient fill - if (prices.length >= 2) { - ctx.beginPath(); - ctx.moveTo(timeToX(times[0]), baselineY); - ctx.lineTo(timeToX(times[0]), priceToY(priceValues[0])); - - for (let i = 1; i < prices.length; i++) { - ctx.lineTo(timeToX(times[i]), priceToY(priceValues[i])); - } - - ctx.lineTo(timeToX(times[times.length - 1]), baselineY); - ctx.closePath(); - - const gradient = ctx.createLinearGradient(0, PADDING.top, 0, height - PADDING.bottom); - gradient.addColorStop(0, COLORS.greenFill); - gradient.addColorStop(0.5, 'rgba(0,0,0,0)'); - gradient.addColorStop(1, COLORS.redFill); - ctx.fillStyle = gradient; - ctx.fill(); - } - - // Draw price line - if (prices.length >= 2) { - ctx.beginPath(); - ctx.strokeStyle = COLORS.line; - ctx.lineWidth = 2; - ctx.lineJoin = 'round'; - ctx.lineCap = 'round'; - - ctx.moveTo(timeToX(times[0]), priceToY(priceValues[0])); - for (let i = 1; i < prices.length; i++) { - ctx.lineTo(timeToX(times[i]), priceToY(priceValues[i])); - } - ctx.stroke(); - } - - // Draw Y axis labels - ctx.fillStyle = COLORS.text; - ctx.font = '11px JetBrains Mono, monospace'; - ctx.textAlign = 'left'; - - for (let price = startPrice; price <= maxPrice; price += priceStep) { - const y = priceToY(price); - ctx.fillText(formatPrice(price), width - PADDING.right + 8, y + 4); - } - - // Draw X axis labels - ctx.textAlign = 'center'; - const hourMs = 60 * 60 * 1000; - const rangeMs = maxTime - minTime; - let stepMs = hourMs * 4; - if (rangeMs < hourMs * 6) stepMs = hourMs; - if (rangeMs < hourMs * 2) stepMs = hourMs / 2; - - let current = Math.ceil(minTime / stepMs) * stepMs; - while (current <= maxTime) { - const x = timeToX(current); - if (x >= PADDING.left && x <= width - PADDING.right) { - const date = new Date(current); - const label = date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false }); - ctx.fillText(label, x, height - PADDING.bottom + 20); - } - current += stepMs; - } - - // Draw trades - for (const trade of (trades || [])) { - const x = timeToX(trade.t || trade.timestamp); - const y = priceToY(trade.price); - - if (x < PADDING.left || x > width - PADDING.right) continue; - - const color = trade.side === 'buy' ? COLORS.buy : COLORS.sell; - const radius = 7; - - // Glow - ctx.beginPath(); - ctx.arc(x, y, radius + 4, 0, Math.PI * 2); - ctx.fillStyle = color + '33'; - ctx.fill(); - - // Outer - ctx.beginPath(); - ctx.arc(x, y, radius, 0, Math.PI * 2); - ctx.fillStyle = color; - ctx.fill(); - - // Inner - ctx.beginPath(); - ctx.arc(x, y, radius - 3, 0, Math.PI * 2); - ctx.fillStyle = COLORS.bg; - ctx.fill(); - } - - // Draw current price tag - if (prices.length > 0) { - const lastPrice = priceValues[priceValues.length - 1]; - const y = priceToY(lastPrice); - const x = width - PADDING.right; - - const isUp = lastPrice >= baselinePrice; - const color = isUp ? COLORS.green : COLORS.red; - - ctx.fillStyle = color; - roundRect(ctx, x + 4, y - 11, 70, 22, 4); - ctx.fill(); - - ctx.fillStyle = '#fff'; - ctx.font = 'bold 11px JetBrains Mono, monospace'; - ctx.textAlign = 'left'; - ctx.fillText(formatPrice(lastPrice), x + 10, y + 4); - } - - }, [prices, trades, dimensions]); - - // Mouse handling for tooltip - const handleMouseMove = (e) => { - if (!prices || prices.length === 0) { - setTooltip(null); - return; - } - - const rect = canvasRef.current.getBoundingClientRect(); - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; - - const width = dimensions.width; - const height = dimensions.height; - const chartWidth = width - PADDING.left - PADDING.right; - - if (x < PADDING.left || x > width - PADDING.right || - y < PADDING.top || y > height - PADDING.bottom) { - setTooltip(null); - return; - } - - const times = prices.map(d => d.t || d.timestamp); - const minTime = Math.min(...times); - const maxTime = Math.max(...times); - - const normalized = (x - PADDING.left) / chartWidth; - const timestamp = minTime + normalized * (maxTime - minTime); - - let nearestPoint = null; - let nearestDistance = Infinity; - - for (const point of prices) { - const t = point.t || point.timestamp; - const distance = Math.abs(t - timestamp); - if (distance < nearestDistance) { - nearestDistance = distance; - nearestPoint = point; - } - } - - if (nearestPoint) { - setTooltip({ - x: e.clientX - rect.left + 15, - y: e.clientY - rect.top - 10, - price: nearestPoint.p || nearestPoint.price, - timestamp: nearestPoint.t || nearestPoint.timestamp, - }); - } + // Handle resize + useEffect(() => { + const updateDimensions = () => { + if (containerRef.current) { + const rect = containerRef.current.getBoundingClientRect(); + setDimensions({ width: rect.width, height: rect.height }); + } }; - return ( -
- setTooltip(null)} - /> - {tooltip && ( -
-
- ${tooltip.price.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} -
-
- {new Date(tooltip.timestamp).toLocaleString('en-US', { - month: 'short', - day: 'numeric', - hour: '2-digit', - minute: '2-digit' - })} -
-
- )} + updateDimensions(); + window.addEventListener('resize', updateDimensions); + return () => window.removeEventListener('resize', updateDimensions); + }, []); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas || dimensions.width === 0) return; + + const ctx = canvas.getContext('2d'); + const dpr = window.devicePixelRatio || 1; + + canvas.width = dimensions.width * dpr; + canvas.height = dimensions.height * dpr; + ctx.scale(dpr, dpr); + + const width = dimensions.width; + const height = dimensions.height; + const chartWidth = width - PADDING.left - PADDING.right; + const chartHeight = height - PADDING.top - PADDING.bottom; + + // Clear + ctx.clearRect(0, 0, width, height); + + if (!prices || prices.length === 0) { + ctx.fillStyle = COLORS.textMuted; + ctx.font = '18px Ac437_Rainbow100_re_132, monospace'; + ctx.textAlign = 'center'; + ctx.fillText('No data for this day', width / 2, height / 2); + return; + } + + // Calculate bounds + const priceValues = prices.map(d => d.p || d.price); + const times = prices.map(d => d.t || d.timestamp); + + let minPrice = Math.min(...priceValues); + let maxPrice = Math.max(...priceValues); + const minTime = Math.min(...times); + const maxTime = Math.max(...times); + + const pricePadding = (maxPrice - minPrice) * 0.05 || 10; + minPrice -= pricePadding; + maxPrice += pricePadding; + + const baselinePrice = priceValues[0]; + + // Coordinate converters + const priceToY = (price) => { + const range = maxPrice - minPrice; + const normalized = (price - minPrice) / range; + return PADDING.top + chartHeight * (1 - normalized); + }; + + const timeToX = (timestamp) => { + const range = maxTime - minTime; + const normalized = (timestamp - minTime) / range; + return PADDING.left + chartWidth * normalized; + }; + + // Draw grid + ctx.strokeStyle = COLORS.grid; + ctx.lineWidth = 1; + + const priceStep = calculateNiceStep(maxPrice - minPrice, 5); + const startPrice = Math.ceil(minPrice / priceStep) * priceStep; + + for (let price = startPrice; price <= maxPrice; price += priceStep) { + const y = priceToY(price); + ctx.beginPath(); + ctx.moveTo(PADDING.left, y); + ctx.lineTo(width - PADDING.right, y); + ctx.stroke(); + } + + const baselineY = priceToY(baselinePrice); + ctx.strokeStyle = COLORS.baseline; + ctx.setLineDash([4, 4]); + ctx.beginPath(); + ctx.moveTo(PADDING.left, baselineY); + ctx.lineTo(width - PADDING.right, baselineY); + ctx.stroke(); + ctx.setLineDash([]); + + if (prices.length >= 2) { + ctx.beginPath(); + ctx.moveTo(timeToX(times[0]), baselineY); + ctx.lineTo(timeToX(times[0]), priceToY(priceValues[0])); + + for (let i = 1; i < prices.length; i++) { + ctx.lineTo(timeToX(times[i]), priceToY(priceValues[i])); + } + + ctx.lineTo(timeToX(times[times.length - 1]), baselineY); + ctx.closePath(); + + const gradient = ctx.createLinearGradient(0, PADDING.top, 0, height - PADDING.bottom); + gradient.addColorStop(0, COLORS.greenFill); + gradient.addColorStop(0.5, 'rgba(0,0,0,0)'); + gradient.addColorStop(1, COLORS.redFill); + ctx.fillStyle = gradient; + ctx.fill(); + } + + if (prices.length >= 2) { + ctx.beginPath(); + ctx.strokeStyle = COLORS.line; + ctx.lineWidth = 2; + ctx.lineJoin = 'round'; + ctx.lineCap = 'round'; + + ctx.moveTo(timeToX(times[0]), priceToY(priceValues[0])); + for (let i = 1; i < prices.length; i++) { + ctx.lineTo(timeToX(times[i]), priceToY(priceValues[i])); + } + ctx.stroke(); + } + + ctx.fillStyle = COLORS.text; + ctx.font = '14px Ac437_Rainbow100_re_132, monospace'; + ctx.textAlign = 'left'; + + for (let price = startPrice; price <= maxPrice; price += priceStep) { + const y = priceToY(price); + ctx.fillText(formatPrice(price), width - PADDING.right + 8, y + 4); + } + + ctx.textAlign = 'center'; + const hourMs = 60 * 60 * 1000; + const rangeMs = maxTime - minTime; + let stepMs = hourMs * 4; + if (rangeMs < hourMs * 6) stepMs = hourMs; + if (rangeMs < hourMs * 2) stepMs = hourMs / 2; + + let current = Math.ceil(minTime / stepMs) * stepMs; + while (current <= maxTime) { + const x = timeToX(current); + if (x >= PADDING.left && x <= width - PADDING.right) { + const date = new Date(current); + const label = date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false }); + ctx.fillText(label, x, height - PADDING.bottom + 20); + } + current += stepMs; + } + + for (const trade of (trades || [])) { + const x = timeToX(trade.t || trade.timestamp); + const y = priceToY(trade.price); + + if (x < PADDING.left || x > width - PADDING.right) continue; + + const color = trade.side === 'buy' ? COLORS.buy : COLORS.sell; + const radius = 7; + + ctx.beginPath(); + ctx.arc(x, y, radius + 4, 0, Math.PI * 2); + ctx.fillStyle = color + '33'; + ctx.fill(); + + // Outer + ctx.beginPath(); + ctx.arc(x, y, radius, 0, Math.PI * 2); + ctx.fillStyle = color; + ctx.fill(); + + // Inner + ctx.beginPath(); + ctx.arc(x, y, radius - 3, 0, Math.PI * 2); + ctx.fillStyle = COLORS.bg; + ctx.fill(); + } + + if (prices.length > 0) { + const lastPrice = priceValues[priceValues.length - 1]; + const y = priceToY(lastPrice); + const x = width - PADDING.right; + + const isUp = lastPrice >= baselinePrice; + const color = isUp ? COLORS.green : COLORS.red; + + ctx.fillStyle = color; + roundRect(ctx, x + 4, y - 11, 70, 22, 4); + ctx.fill(); + + ctx.fillStyle = '#fff'; + ctx.font = 'bold 14px Ac437_Rainbow100_re_132, monospace'; + ctx.textAlign = 'left'; + ctx.fillText(formatPrice(lastPrice), x + 10, y + 4); + } + + }, [prices, trades, dimensions]); + + const handleMouseMove = (e) => { + if (!prices || prices.length === 0) { + setTooltip(null); + return; + } + + const rect = canvasRef.current.getBoundingClientRect(); + const x = e.clientX - rect.left; + const y = e.clientY - rect.top; + + const width = dimensions.width; + const height = dimensions.height; + const chartWidth = width - PADDING.left - PADDING.right; + + if (x < PADDING.left || x > width - PADDING.right || + y < PADDING.top || y > height - PADDING.bottom) { + setTooltip(null); + return; + } + + const times = prices.map(d => d.t || d.timestamp); + const minTime = Math.min(...times); + const maxTime = Math.max(...times); + + const normalized = (x - PADDING.left) / chartWidth; + const timestamp = minTime + normalized * (maxTime - minTime); + + let nearestPoint = null; + let nearestDistance = Infinity; + + for (const point of prices) { + const t = point.t || point.timestamp; + const distance = Math.abs(t - timestamp); + if (distance < nearestDistance) { + nearestDistance = distance; + nearestPoint = point; + } + } + + if (nearestPoint) { + setTooltip({ + x: e.clientX - rect.left + 15, + y: e.clientY - rect.top - 10, + price: nearestPoint.p || nearestPoint.price, + timestamp: nearestPoint.t || nearestPoint.timestamp, + }); + } + }; + + return ( +
+ setTooltip(null)} + /> + {tooltip && ( +
+
+ ${tooltip.price.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} +
+
+ {new Date(tooltip.timestamp).toLocaleString('en-US', { + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit' + })} +
- ); + )} +
+ ); } function calculateNiceStep(range, targetSteps) { - const roughStep = range / targetSteps; - const magnitude = Math.pow(10, Math.floor(Math.log10(roughStep))); - const normalized = roughStep / magnitude; - - let niceStep; - if (normalized <= 1.5) niceStep = 1; - else if (normalized <= 3) niceStep = 2; - else if (normalized <= 7) niceStep = 5; - else niceStep = 10; - - return niceStep * magnitude; + const roughStep = range / targetSteps; + const magnitude = Math.pow(10, Math.floor(Math.log10(roughStep))); + const normalized = roughStep / magnitude; + + let niceStep; + if (normalized <= 1.5) niceStep = 1; + else if (normalized <= 3) niceStep = 2; + else if (normalized <= 7) niceStep = 5; + else niceStep = 10; + + return niceStep * magnitude; } function formatPrice(price) { - if (price >= 10000) { - return '$' + (price / 1000).toFixed(1) + 'k'; - } - return '$' + price.toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 0 }); + if (price >= 10000) { + return '$' + (price / 1000).toFixed(1) + 'k'; + } + return '$' + price.toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 0 }); } function roundRect(ctx, x, y, width, height, radius) { - ctx.beginPath(); - ctx.moveTo(x + radius, y); - ctx.lineTo(x + width - radius, y); - ctx.quadraticCurveTo(x + width, y, x + width, y + radius); - ctx.lineTo(x + width, y + height - radius); - ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); - ctx.lineTo(x + radius, y + height); - ctx.quadraticCurveTo(x, y + height, x, y + height - radius); - ctx.lineTo(x, y + radius); - ctx.quadraticCurveTo(x, y, x + radius, y); - ctx.closePath(); + ctx.beginPath(); + ctx.moveTo(x + radius, y); + ctx.lineTo(x + width - radius, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + radius); + ctx.lineTo(x + width, y + height - radius); + ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); + ctx.lineTo(x + radius, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - radius); + ctx.lineTo(x, y + radius); + ctx.quadraticCurveTo(x, y, x + radius, y); + ctx.closePath(); } diff --git a/trahn-trade-frontend/src/fonts/Ac437_Rainbow100_re_132.ttf b/trahn-trade-frontend/src/fonts/Ac437_Rainbow100_re_132.ttf new file mode 100644 index 0000000000000000000000000000000000000000..3bce32855e6c822f179cce030370dfeb21666993 GIT binary patch literal 24700 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 literal 0 HcmV?d00001 diff --git a/trahn-trade-frontend/src/fonts/Mx437_Compaq_Port3.ttf b/trahn-trade-frontend/src/fonts/Mx437_Compaq_Port3.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9be9155047b1e41ab2982412bafdeeadaf5f9f44 GIT binary patch literal 50188 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 literal 0 HcmV?d00001 diff --git a/trahn-trade-frontend/src/fonts/Px437_AMI_EGA_8x8.ttf b/trahn-trade-frontend/src/fonts/Px437_AMI_EGA_8x8.ttf new file mode 100644 index 0000000000000000000000000000000000000000..2962a7260d4600472439a71298f9ed2da1117a23 GIT binary patch literal 23748 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 Z<^f6UXKu6Zqa$
-
{`
-   ████████╗██████╗  █████╗ ██╗  ██╗███╗   ██╗
-   ╚══██╔══╝██╔══██╗██╔══██╗██║  ██║████╗  ██║
-      ██║   ██████╔╝███████║███████║██╔██╗ ██║
-      ██║   ██╔══██╗██╔══██║██╔══██║██║╚██╗██║
-      ██║   ██║  ██║██║  ██║██║  ██║██║ ╚████║
-      ╚═╝   ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═══╝`}
+

TRAHN

diff --git a/trahn-trade-frontend/src/pages/Login.jsx b/trahn-trade-frontend/src/pages/Login.jsx index 9f74cc9..7c951d2 100644 --- a/trahn-trade-frontend/src/pages/Login.jsx +++ b/trahn-trade-frontend/src/pages/Login.jsx @@ -29,13 +29,7 @@ export function Login() { return (
-
{`
-   ████████╗██████╗  █████╗ ██╗  ██╗███╗   ██╗
-   ╚══██╔══╝██╔══██╗██╔══██╗██║  ██║████╗  ██║
-      ██║   ██████╔╝███████║███████║██╔██╗ ██║
-      ██║   ██╔══██╗██╔══██║██╔══██║██║╚██╗██║
-      ██║   ██║  ██║██║  ██║██║  ██║██║ ╚████║
-      ╚═╝   ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═══╝`}
+

TRAHN

Welcome Back

diff --git a/trahn-trade-frontend/src/pages/Signup.jsx b/trahn-trade-frontend/src/pages/Signup.jsx index 64e9988..b795cd6 100644 --- a/trahn-trade-frontend/src/pages/Signup.jsx +++ b/trahn-trade-frontend/src/pages/Signup.jsx @@ -38,13 +38,7 @@ export function Signup() { return (
-
{`
-   ████████╗██████╗  █████╗ ██╗  ██╗███╗   ██╗
-   ╚══██╔══╝██╔══██╗██╔══██╗██║  ██║████╗  ██║
-      ██║   ██████╔╝███████║███████║██╔██╗ ██║
-      ██║   ██╔══██╗██╔══██║██╔══██║██║╚██╗██║
-      ██║   ██║  ██║██║  ██║██║  ██║██║ ╚████║
-      ╚═╝   ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═══╝`}
+

TRAHN

{submitted ? ( <> From af696ed46208483f0aba9a8466f6882e4ad56afa Mon Sep 17 00:00:00 2001 From: KS Jannette Date: Sun, 22 Feb 2026 17:17:27 -0500 Subject: [PATCH 2/3] update --- trahn-trade-frontend/src/App.css | 7 +- trahn-trade-frontend/src/App.jsx | 2 - trahn-trade-frontend/src/api/tradingApi.js | 111 +++++++----------- trahn-trade-frontend/src/auth/firebase.js | 1 - trahn-trade-frontend/src/components/Chart.jsx | 7 -- .../src/contexts/TradingDataContext.jsx | 3 - .../src/hooks/useChartData.js | 15 --- .../src/hooks/useTradingData.js | 14 --- trahn-trade-frontend/src/pages/Dashboard.jsx | 2 - trahn-trade-frontend/src/pages/Signup.jsx | 8 +- 10 files changed, 48 insertions(+), 122 deletions(-) diff --git a/trahn-trade-frontend/src/App.css b/trahn-trade-frontend/src/App.css index 7b6075e..37e9cd5 100644 --- a/trahn-trade-frontend/src/App.css +++ b/trahn-trade-frontend/src/App.css @@ -52,7 +52,7 @@ header { color: var(--text-primary); text-align: center; letter-spacing: 0.25em; - text-shadow: 0 10 30px rgba(88, 166, 255, 0.3); + text-shadow: 4px 12px 12px rgb(173, 1, 124); } .subtitle { @@ -105,15 +105,16 @@ header { .date-label { font-family: 'Ac437_Rainbow100_re_132'; font-weight: 600; - font-size: 1.45rem; + font-size: 1.65rem; font-weight: 600; + letter-spacing: 2px; color: var(--text-primary); } .date-status { display: block; font-family: 'Ac437_Rainbow100_re_132', monospace; - font-size: 0.75rem; + font-size: 1rem; color: var(--text-secondary); margin-top: 0.25rem; } diff --git a/trahn-trade-frontend/src/App.jsx b/trahn-trade-frontend/src/App.jsx index efc21df..c8c95f4 100644 --- a/trahn-trade-frontend/src/App.jsx +++ b/trahn-trade-frontend/src/App.jsx @@ -8,7 +8,6 @@ import { Login } from './pages/Login'; import { Signup } from './pages/Signup'; import './App.css'; -// Create React Query client const queryClient = new QueryClient({ defaultOptions: { queries: { @@ -20,7 +19,6 @@ const queryClient = new QueryClient({ }, }); -// Redirect authenticated users away from auth pages function PublicRoute({ children }) { const { currentUser } = useAuth(); diff --git a/trahn-trade-frontend/src/api/tradingApi.js b/trahn-trade-frontend/src/api/tradingApi.js index b3fbc6a..31a0ed5 100644 --- a/trahn-trade-frontend/src/api/tradingApi.js +++ b/trahn-trade-frontend/src/api/tradingApi.js @@ -1,81 +1,54 @@ -/** - * Trading API Client - * Communicates with backend REST API - */ - const API_BASE = 'http://localhost:3001/v1'; export const tradingApi = { - /** - * Get current trading day prices - */ - getCurrentPrices: async () => { - const response = await fetch(`${API_BASE}/prices/today`); - if (!response.ok) throw new Error(`HTTP ${response.status}`); - return response.json(); - }, - /** - * Get current trading day trades - */ - getCurrentTrades: async () => { - const response = await fetch(`${API_BASE}/trades/today`); - if (!response.ok) throw new Error(`HTTP ${response.status}`); - return response.json(); - }, + getCurrentPrices: async () => { + const response = await fetch(`${API_BASE}/prices/today`); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + return response.json(); + }, - /** - * Get current grid state - */ - getCurrentGrid: async () => { - const response = await fetch(`${API_BASE}/grid/current`); - if (!response.ok) throw new Error(`HTTP ${response.status}`); - return response.json(); - }, + getCurrentTrades: async () => { + const response = await fetch(`${API_BASE}/trades/today`); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + return response.json(); + }, - /** - * Get latest support/resistance levels - */ - getLatestSR: async () => { - const response = await fetch(`${API_BASE}/support-resistance/latest`); - if (!response.ok) throw new Error(`HTTP ${response.status}`); - return response.json(); - }, + getCurrentGrid: async () => { + const response = await fetch(`${API_BASE}/grid/current`); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + return response.json(); + }, - /** - * Get available trading days - */ - getAvailableDays: async () => { - const response = await fetch(`${API_BASE}/prices/days`); - if (!response.ok) throw new Error(`HTTP ${response.status}`); - return response.json(); - }, - /** - * Get historical prices for a specific day - */ - getPricesByDay: async (date) => { - const response = await fetch(`${API_BASE}/prices/day/${date}`); - if (!response.ok) throw new Error(`HTTP ${response.status}`); - return response.json(); - }, + getLatestSR: async () => { + const response = await fetch(`${API_BASE}/support-resistance/latest`); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + return response.json(); + }, - /** - * Get historical trades for a specific day - */ - getTradesByDay: async (date) => { - const response = await fetch(`${API_BASE}/trades/day/${date}`); - if (!response.ok) throw new Error(`HTTP ${response.status}`); - return response.json(); - }, + getAvailableDays: async () => { + const response = await fetch(`${API_BASE}/prices/days`); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + return response.json(); + }, - /** - * Get trade statistics - */ - getTradeStats: async () => { - const response = await fetch(`${API_BASE}/trades/stats`); - if (!response.ok) throw new Error(`HTTP ${response.status}`); - return response.json(); - }, + getPricesByDay: async (date) => { + const response = await fetch(`${API_BASE}/prices/day/${date}`); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + return response.json(); + }, + + getTradesByDay: async (date) => { + const response = await fetch(`${API_BASE}/trades/day/${date}`); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + return response.json(); + }, + + getTradeStats: async () => { + const response = await fetch(`${API_BASE}/trades/stats`); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + return response.json(); + }, }; diff --git a/trahn-trade-frontend/src/auth/firebase.js b/trahn-trade-frontend/src/auth/firebase.js index 03ee791..37726e9 100644 --- a/trahn-trade-frontend/src/auth/firebase.js +++ b/trahn-trade-frontend/src/auth/firebase.js @@ -7,7 +7,6 @@ const app = initializeApp(firebaseConfig); export const auth = getAuth(app); export const db = getFirestore(app); -// Add email to waitlist collection export async function addToWaitlist(email) { try { await addDoc(collection(db, 'trahn-trade-signups'), { diff --git a/trahn-trade-frontend/src/components/Chart.jsx b/trahn-trade-frontend/src/components/Chart.jsx index 3da40a6..6f4613a 100644 --- a/trahn-trade-frontend/src/components/Chart.jsx +++ b/trahn-trade-frontend/src/components/Chart.jsx @@ -23,7 +23,6 @@ export function Chart({ prices, trades }) { const [tooltip, setTooltip] = useState(null); const [dimensions, setDimensions] = useState({ width: 0, height: 0 }); - // Handle resize useEffect(() => { const updateDimensions = () => { if (containerRef.current) { @@ -53,7 +52,6 @@ export function Chart({ prices, trades }) { const chartWidth = width - PADDING.left - PADDING.right; const chartHeight = height - PADDING.top - PADDING.bottom; - // Clear ctx.clearRect(0, 0, width, height); if (!prices || prices.length === 0) { @@ -64,7 +62,6 @@ export function Chart({ prices, trades }) { return; } - // Calculate bounds const priceValues = prices.map(d => d.p || d.price); const times = prices.map(d => d.t || d.timestamp); @@ -79,7 +76,6 @@ export function Chart({ prices, trades }) { const baselinePrice = priceValues[0]; - // Coordinate converters const priceToY = (price) => { const range = maxPrice - minPrice; const normalized = (price - minPrice) / range; @@ -92,7 +88,6 @@ export function Chart({ prices, trades }) { return PADDING.left + chartWidth * normalized; }; - // Draw grid ctx.strokeStyle = COLORS.grid; ctx.lineWidth = 1; @@ -191,13 +186,11 @@ export function Chart({ prices, trades }) { ctx.fillStyle = color + '33'; ctx.fill(); - // Outer ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI * 2); ctx.fillStyle = color; ctx.fill(); - // Inner ctx.beginPath(); ctx.arc(x, y, radius - 3, 0, Math.PI * 2); ctx.fillStyle = COLORS.bg; diff --git a/trahn-trade-frontend/src/contexts/TradingDataContext.jsx b/trahn-trade-frontend/src/contexts/TradingDataContext.jsx index 155ff6a..50f8f0a 100644 --- a/trahn-trade-frontend/src/contexts/TradingDataContext.jsx +++ b/trahn-trade-frontend/src/contexts/TradingDataContext.jsx @@ -62,7 +62,6 @@ function reducer(state, action) { export function TradingDataProvider({ children }) { const [state, dispatch] = useReducer(reducer, initialState); - // React Query: Poll current day data every 1 minute const { data: currentData, error, isLoading } = useQuery({ queryKey: ['currentDay'], queryFn: async () => { @@ -87,7 +86,6 @@ export function TradingDataProvider({ children }) { retryDelay: 2000, }); - // Update context when live data changes useEffect(() => { if (currentData) { dispatch({ @@ -97,7 +95,6 @@ export function TradingDataProvider({ children }) { } }, [currentData]); - // Handle connection status useEffect(() => { if (error) { dispatch({ type: 'SET_CONNECTION_STATUS', status: 'error' }); diff --git a/trahn-trade-frontend/src/hooks/useChartData.js b/trahn-trade-frontend/src/hooks/useChartData.js index 71a30e7..aeab7d7 100644 --- a/trahn-trade-frontend/src/hooks/useChartData.js +++ b/trahn-trade-frontend/src/hooks/useChartData.js @@ -1,18 +1,3 @@ -/** - * DEPRECATED - Replaced by TradingDataContext + React Query - * - * This file is kept for reference but no longer used. - * The new implementation: - * - Uses React Query for polling - * - Uses Context for state management - * - Calls REST API instead of JSON files - * - * See: - * - contexts/TradingDataContext.jsx - * - hooks/useTradingData.js - * - api/tradingApi.js - */ - export function useChartData() { throw new Error('useChartData is deprecated. Use useTradingData from TradingDataContext instead.'); } diff --git a/trahn-trade-frontend/src/hooks/useTradingData.js b/trahn-trade-frontend/src/hooks/useTradingData.js index c5ca334..a1b5a18 100644 --- a/trahn-trade-frontend/src/hooks/useTradingData.js +++ b/trahn-trade-frontend/src/hooks/useTradingData.js @@ -1,15 +1,7 @@ -/** - * Custom hooks for trading data management - * Handles carousel navigation and historical day loading - */ - import { useQuery } from '@tanstack/react-query'; import { useTradingData } from '../contexts/TradingDataContext'; import { tradingApi } from '../api/tradingApi'; -/** - * Hook for loading historical day data (lazy load) - */ export function useHistoricalDay(date, enabled = false) { return useQuery({ queryKey: ['historicalDay', date], @@ -26,9 +18,6 @@ export function useHistoricalDay(date, enabled = false) { }); } -/** - * Hook for carousel navigation - */ export function useCarousel() { const { state, dispatch } = useTradingData(); @@ -38,12 +27,9 @@ export function useCarousel() { } if (index === state.availableDays.length - 1) { - // Navigate to live (current) day dispatch({ type: 'SWITCH_TO_LIVE' }); } else { - // Load historical day const date = state.availableDays[index]; - // Historical data will be loaded via useHistoricalDay in Dashboard dispatch({ type: 'LOAD_HISTORICAL_DAY', prices: [], diff --git a/trahn-trade-frontend/src/pages/Dashboard.jsx b/trahn-trade-frontend/src/pages/Dashboard.jsx index 11f78af..31d6c09 100644 --- a/trahn-trade-frontend/src/pages/Dashboard.jsx +++ b/trahn-trade-frontend/src/pages/Dashboard.jsx @@ -12,11 +12,9 @@ export function Dashboard() { const { state, dispatch } = useTradingData(); const carousel = useCarousel(); - // Load historical day data when viewing past days const historicalDate = !state.isLive ? state.availableDays[state.currentDayIndex] : null; const { data: historicalData } = useHistoricalDay(historicalDate, !state.isLive); - // Update context with historical data when loaded useEffect(() => { if (historicalData && !state.isLive) { dispatch({ diff --git a/trahn-trade-frontend/src/pages/Signup.jsx b/trahn-trade-frontend/src/pages/Signup.jsx index b795cd6..53beeed 100644 --- a/trahn-trade-frontend/src/pages/Signup.jsx +++ b/trahn-trade-frontend/src/pages/Signup.jsx @@ -1,16 +1,13 @@ import { useState } from 'react'; import { Link } from 'react-router-dom'; import { addToWaitlist } from '../auth/firebase'; -//a comment export function Signup() { const [email, setEmail] = useState(''); const [loading, setLoading] = useState(false); const [submitted, setSubmitted] = useState(false); const [error, setError] = useState(''); -// -// -// + async function handleSubmit(e) { e.preventDefault(); @@ -33,8 +30,7 @@ export function Signup() { } setLoading(false); } -// some spacing -//more + return (
From 04ff0452f92c6f43c38c875eeda870dca560f093 Mon Sep 17 00:00:00 2001 From: KS Jannette Date: Sun, 22 Feb 2026 17:23:42 -0500 Subject: [PATCH 3/3] more twekas for charting --- trahn-trade-backend/internal/repository/price.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trahn-trade-backend/internal/repository/price.go b/trahn-trade-backend/internal/repository/price.go index 021a2f3..77f1dca 100644 --- a/trahn-trade-backend/internal/repository/price.go +++ b/trahn-trade-backend/internal/repository/price.go @@ -40,7 +40,7 @@ func (r *PriceRepo) GetByDay(ctx context.Context, tradingDay string) ([]models.P func (r *PriceRepo) GetAvailableDays(ctx context.Context) ([]string, error) { rows, err := r.pool.Query(ctx, - `SELECT DISTINCT trading_day FROM price_history ORDER BY trading_day DESC LIMIT 30`, + `SELECT DISTINCT trading_day FROM price_history ORDER BY trading_day ASC LIMIT 30`, ) if err != nil { return nil, err