feat: show stock names as primary display in portfolio card, detail, and rebalance pages

This commit is contained in:
ayuriel 2026-02-16 12:51:56 +09:00
parent 628b431171
commit 87dff8bfa7
4 changed files with 30 additions and 12 deletions

View File

@ -33,6 +33,7 @@ interface Target {
interface Transaction {
id: number;
ticker: string;
name: string | null;
tx_type: string;
quantity: number;
price: number;
@ -341,10 +342,7 @@ export default function PortfolioDetailPage() {
{portfolio.holdings.map((holding, index) => (
<tr key={holding.ticker}>
<td className="px-4 py-3">
<div className="font-medium text-sm">{holding.name || holding.ticker}</div>
{holding.name && (
<div className="text-xs text-muted-foreground">{holding.ticker}</div>
)}
<span className="font-medium text-sm" title={holding.ticker}>{holding.name || holding.ticker}</span>
</td>
<td className="px-4 py-3 text-sm text-right">
{holding.quantity.toLocaleString()}
@ -454,7 +452,7 @@ export default function PortfolioDetailPage() {
<td className="px-4 py-3 text-sm text-muted-foreground">
{new Date(tx.executed_at).toLocaleString('ko-KR')}
</td>
<td className="px-4 py-3 text-sm font-medium">{tx.ticker}</td>
<td className="px-4 py-3 text-sm font-medium" title={tx.ticker}>{tx.name || tx.ticker}</td>
<td className="px-4 py-3 text-sm text-center">
<span
className={`px-2 py-1 rounded text-xs font-medium ${
@ -519,7 +517,7 @@ export default function PortfolioDetailPage() {
return (
<div key={target.ticker} className="space-y-2">
<div className="flex justify-between text-sm">
<span className="font-medium">{holding?.name || target.ticker}</span>
<span className="font-medium" title={target.ticker}>{holding?.name || target.ticker}</span>
<span className="text-muted-foreground">
{actualRatio.toFixed(1)}% / {target.target_ratio.toFixed(1)}%
<span

View File

@ -59,6 +59,7 @@ export default function RebalancePage() {
const [result, setResult] = useState<RebalanceResponse | null>(null);
const [calculating, setCalculating] = useState(false);
const [error, setError] = useState<string | null>(null);
const [nameMap, setNameMap] = useState<Record<string, string>>({});
useEffect(() => {
const init = async () => {
@ -81,6 +82,18 @@ export default function RebalancePage() {
initialPrices[ticker] = '';
});
setPrices(initialPrices);
// Fetch stock names from portfolio detail
try {
const detail = await api.get<{ holdings: { ticker: string; name: string | null }[] }>(`/api/portfolios/${portfolioId}/detail`);
const names: Record<string, string> = {};
for (const h of detail.holdings) {
if (h.name) names[h.ticker] = h.name;
}
setNameMap(names);
} catch {
// Names are optional, continue without
}
} catch {
router.push('/login');
} finally {
@ -115,6 +128,12 @@ export default function RebalancePage() {
body
);
setResult(data);
// Update name map from results
const newNames = { ...nameMap };
for (const item of data.items) {
if (item.name) newNames[item.ticker] = item.name;
}
setNameMap(newNames);
} catch (err) {
setError(err instanceof Error ? err.message : 'Calculation failed');
} finally {
@ -180,7 +199,7 @@ export default function RebalancePage() {
return (
<div key={ticker}>
<Label htmlFor={`price-${ticker}`}>
{ticker} {target ? `(목표 ${target.target_ratio}%)` : ''} - {getHoldingQty(ticker)}
{nameMap[ticker] || ticker} {target ? `(목표 ${target.target_ratio}%)` : ''} - {getHoldingQty(ticker)}
</Label>
<Input
id={`price-${ticker}`}
@ -297,10 +316,7 @@ export default function RebalancePage() {
{result.items.map((item) => (
<tr key={item.ticker}>
<td className="px-3 py-3">
<div className="font-medium">{item.ticker}</div>
{item.name && (
<div className="text-xs text-muted-foreground">{item.name}</div>
)}
<span className="font-medium" title={item.ticker}>{item.name || item.ticker}</span>
</td>
<td className="px-3 py-3 text-sm text-right">
{item.current_quantity.toLocaleString()}

View File

@ -11,6 +11,7 @@ import { api } from '@/lib/api';
interface HoldingWithValue {
ticker: string;
name: string | null;
current_ratio: number | null;
}

View File

@ -6,6 +6,7 @@ import { PieChart, Pie, Cell, ResponsiveContainer } from 'recharts';
interface Holding {
ticker: string;
name: string | null;
current_ratio: number | null;
}
@ -61,7 +62,8 @@ export function PortfolioCard({
.filter((h) => h.current_ratio !== null && h.current_ratio > 0)
.slice(0, 6)
.map((h, index) => ({
name: h.ticker,
name: h.name || h.ticker,
ticker: h.ticker,
value: h.current_ratio ?? 0,
color: CHART_COLORS[index % CHART_COLORS.length],
}));
@ -144,6 +146,7 @@ export function PortfolioCard({
<span
key={index}
className="text-xs px-2 py-0.5 rounded bg-muted text-muted-foreground"
title={item.ticker}
>
{item.name}
</span>