import {
DataTableView, KeyValueRow, Notice, SectionHeading, Spinner, StaticChartSurface,
loadingText,
unavailableText
} from "../../../components";
import type { ProjectedChartPoint } from "../../../components/chart/core/data";
import type { StaticChartSurfaceProps } from "../../../components/chart/static";
import type { DataTableCell } from "../../../components/ui/data-table/types";
import { colors, priceColor } from "../../../theme/colors";
import { Box, Text } from "../../../ui";
import { formatCompactAmount, formatPercentRaw } from "../../../utils/format";
import { formatSignedCompact, formatWeight, renderBar } from "./display";
import type {
SectorSortPreference,
SectorTableColumn,
SectorTableRow,
} from "./sector-model";
export interface AnalyticsMetricRow {
id: string;
label: string;
value: string;
detail?: string;
color?: string;
}
export function AnalyticsMetricsPanel({
summaryRows,
riskRows,
height,
}: {
summaryRows: AnalyticsMetricRow[];
riskRows: AnalyticsMetricRow[];
height: number;
}) {
return (
{summaryRows.map((row) => (
))}
{riskRows.length > 0 && (
<>
{riskRows.map((row) => (
))}
>
)}
);
}
export function PortfolioHistorySection({
show,
loading,
error,
width,
height,
points,
palette,
axisLabel,
period,
stale,
formatAxisValue,
}: {
show: boolean;
loading: boolean;
error: string | null | undefined;
width: number;
height: number;
points: ProjectedChartPoint[];
palette: StaticChartSurfaceProps["colors"];
axisLabel: string;
period: string | undefined;
stale: boolean | undefined;
formatAxisValue: (value: number) => string;
}) {
if (show) {
return (
<>
{` Flex ${period ?? ""}${stale ? " - cached" : ""}`}
>
);
}
if (loading) {
return (
);
}
if (error) {
return (
{`${unavailableText("Account history")} ${error}`}
);
}
if (points.length > 0) {
return (
{`${points.filter((point) => Number.isFinite(point.close)).length >= 2 ? "Enlarge this pane to view account history." : "Account history needs at least two observations for a chart."}`}
);
}
return null;
}
const sectorRowKey = (row: SectorTableRow) => row.id;
// Module-level so the table's memoized rows keep their identity across renders.
function renderSectorCell(row: SectorTableRow, column: SectorTableColumn): DataTableCell {
switch (column.id) {
case "sector":
return { text: row.sector };
case "weight":
return { text: formatWeight(row.weight) };
case "value":
return { text: formatCompactAmount(row.value ?? undefined) };
case "pnl":
return {
text: formatSignedCompact(row.pnl),
color: row.pnl == null ? colors.textMuted : priceColor(row.pnl),
};
case "return":
return {
text: row.returnPct == null ? "—" : formatPercentRaw(row.returnPct),
color: row.returnPct == null ? colors.textMuted : priceColor(row.returnPct),
};
case "bar":
return {
text: renderBar(row.weight, column.width),
color: colors.textMuted,
};
}
}
export function SectorAllocationTable({
focused,
resetScrollKey,
columns,
rows,
sort,
selectedSectorId,
onHeaderClick,
onSelectSector,
}: {
focused: boolean;
resetScrollKey: string;
columns: SectorTableColumn[];
rows: SectorTableRow[];
sort: SectorSortPreference;
selectedSectorId: string | null;
onHeaderClick: (columnId: string) => void;
onSelectSector: (sectorId: string) => void;
}) {
return (
focused={focused}
selection={{
kind: "id",
selectedId: selectedSectorId,
getId: (row) => row.id,
onChange: (id) => onSelectSector(id),
}}
resetScrollKey={resetScrollKey}
columns={columns}
items={rows}
sortColumnId={sort.columnId}
sortDirection={sort.direction}
onHeaderClick={onHeaderClick}
getItemKey={sectorRowKey}
emptyStateTitle="No sector data available"
emptyStateHint="Load profile data or add sectors to the portfolio positions."
renderCell={renderSectorCell}
/>
);
}