-
- Welcome back, {user?.username || 'Trader'}!
-
-
- Monitor and manage your active stock portfolios in real-time.
-
+
+
+
+ Welcome back, {user?.username || 'Trader'}!
+
+
+ Monitor and manage your active stock portfolios in real-time.
+
+
+
+
+ Alarms ({activeAlertsCount})
+
+
diff --git a/src/pages/LiveMarketDashboard.tsx b/src/pages/LiveMarketDashboard.tsx
index 7d01f55..ee92801 100644
--- a/src/pages/LiveMarketDashboard.tsx
+++ b/src/pages/LiveMarketDashboard.tsx
@@ -10,14 +10,37 @@ import {
Check,
TrendingUp,
TrendingDown,
+ Bell,
} from 'lucide-react';
import { useUserStore } from '@/stores/userStore';
+import { usePriceAlertStore } from '@/stores/priceAlertStore';
import { portfolioService } from '@/services/portfolio.service';
import { Sidebar } from '@/components/Sidebar';
import { StockSearchBar } from '@/components/StockSearchBar';
+import { PriceAlertModal } from '@/components/PriceAlertModal';
+import { ActiveAlertsDrawer } from '@/components/ActiveAlertsDrawer';
import { ROUTES } from '@/constants/routes.constants';
import '@/styles/components/live-market.css';
+const playAlertChime = () => {
+ try {
+ const audioCtx = new (window.AudioContext || (window as any).webkitAudioContext)();
+ const osc = audioCtx.createOscillator();
+ const gain = audioCtx.createGain();
+ osc.type = 'sine';
+ osc.frequency.setValueAtTime(880, audioCtx.currentTime);
+ osc.frequency.exponentialRampToValueAtTime(1760, audioCtx.currentTime + 0.3);
+ gain.gain.setValueAtTime(0.15, audioCtx.currentTime);
+ gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.35);
+ osc.connect(gain);
+ gain.connect(audioCtx.destination);
+ osc.start();
+ osc.stop(audioCtx.currentTime + 0.35);
+ } catch (e) {
+ console.error('Audio chime error:', e);
+ }
+};
+
const MARKET_WS_URL = import.meta.env.VITE_MARKET_WS_BASE_URL || 'ws://localhost:8001';
interface MarketStock {
@@ -41,12 +64,21 @@ export const LiveMarketDashboard: React.FC = () => {
);
const { user, logout, isAuthenticated } = useUserStore();
+ const { openModal, toggleDrawer, fetchAlerts, alerts } = usePriceAlertStore();
+ const activeAlertsCount = alerts.filter((a) => !a.is_triggered).length;
const handleLogout = async () => {
await logout();
navigate(ROUTES.HOME);
};
+ // Fetch initial user alerts when authenticated
+ useEffect(() => {
+ if (isAuthenticated) {
+ fetchAlerts();
+ }
+ }, [isAuthenticated, fetchAlerts]);
+
// 1. Listen to market data WebSocket
useEffect(() => {
let ws: WebSocket;
@@ -68,7 +100,14 @@ export const LiveMarketDashboard: React.FC = () => {
ws.onmessage = (event) => {
try {
const liveData = JSON.parse(event.data);
- if (liveData && Array.isArray(liveData.gainers) && Array.isArray(liveData.losers)) {
+ if (liveData && liveData.type === 'PRICE_ALERT_TRIGGERED') {
+ playAlertChime();
+ setFeedback({
+ message: `🔔 PRICE ALERT: ${liveData.symbol} hit target price $${liveData.target_price}!`,
+ type: 'success',
+ });
+ fetchAlerts();
+ } else if (liveData && Array.isArray(liveData.gainers) && Array.isArray(liveData.losers)) {
setMarketData(liveData);
}
} catch (err) {
@@ -209,6 +248,15 @@ export const LiveMarketDashboard: React.FC = () => {
placeholder="Search stocks or tickers..."
/>
+
-
Get alerted for anomalies
+
Smart Price Target Alerts
- Automatically monitor market volume and price anomalies and get notified instantly.
+ Monitor market thresholds and receive instant Web Audio & Resend email alarms.
-
- Upgrade to Pro
-
+
+ openModal()}
+ style={{ color: '#00e599', borderColor: 'rgba(0,229,153,0.3)' }}
+ >
+ + Set Price Alarm 🔔
+
+ toggleDrawer()}>
+ View Active ({activeAlertsCount})
+
+
@@ -313,21 +370,33 @@ export const LiveMarketDashboard: React.FC = () => {
-