blob: 80e13f3f22769c67094b9b9c538e4b9029a4bb78 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
import Image from "next/image";
import type { NavItem } from "@/lib/overview";
import { deltaClass, fmtCurrency, fmtPct } from "@/lib/format";
import { watchlistSubtitle } from "@/lib/overview";
import type { WatchlistResponse } from "@/types/api";
type Props = {
navItems: NavItem[];
selectedKey: string;
currentTicker: string;
watchlist: WatchlistResponse;
watchlistError: string | null;
onSelectTicker: (symbol: string) => void;
onRemoveTicker: (symbol: string) => void;
};
export function Sidebar({
navItems,
selectedKey,
currentTicker,
watchlist,
watchlistError,
onSelectTicker,
onRemoveTicker
}: Props) {
return (
<aside className="psm-side">
<div className="psm-brand">
<Image className="psm-brand-mark" src="/design-system/logo-monogram.svg" alt="" width={34} height={34} />
<div className="psm-brand-copy">
<div className="psm-brand-name">Prism</div>
<div className="psm-brand-sub">Market Workbench</div>
</div>
</div>
<div className="psm-side-section">
<div className="psm-side-label">Workspace</div>
</div>
<nav className="psm-nav" aria-label="Primary">
{navItems.map((item) => {
const active = item.key === selectedKey;
return (
<button
key={item.key}
type="button"
className={`psm-nav-item${active ? " active" : ""}${item.disabled ? " disabled" : ""}`}
disabled={item.disabled}
>
<span className={`psm-icon icon-${item.icon}`} aria-hidden />
<span className="psm-nav-copy">
<span>{item.label}</span>
{item.disabled ? <span className="psm-nav-coming">Soon</span> : null}
</span>
</button>
);
})}
</nav>
<div className="psm-side-section">
<div className="psm-side-label">Watchlist</div>
</div>
<div className="psm-watch">
<div className="psm-watch-toolbar">
<div className="psm-watch-limit">
{watchlist.items.length}/{watchlist.limit}
</div>
</div>
{watchlist.items.length === 0 ? <div className="psm-watch-empty">Saved tickers will appear here.</div> : null}
{watchlist.items.map((item) => {
const active = item.symbol === currentTicker;
return (
<div key={item.symbol} className={`psm-watch-row${active ? " active" : ""}`}>
<button type="button" className="psm-watch-select" onClick={() => onSelectTicker(item.symbol)}>
<span className="psm-watch-main">
<span className="psm-watch-symbol">{item.symbol}</span>
<span className="psm-watch-date">{watchlistSubtitle(item)}</span>
</span>
<span className="psm-watch-price">{fmtCurrency(item.quote?.price)}</span>
<span className={`psm-watch-change ${deltaClass(item.quote?.change_pct)}`}>{fmtPct(item.quote?.change_pct, 2, true)}</span>
</button>
<button
type="button"
aria-label={`Remove ${item.symbol} from watchlist`}
className="psm-watch-remove"
onClick={() => onRemoveTicker(item.symbol)}
>
×
</button>
</div>
);
})}
{watchlistError ? <p className="psm-muted-copy psm-error-copy">{watchlistError}</p> : null}
</div>
</aside>
);
}
|