phase-10: ui code enhancements
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
export { default as CallForm } from './CallForm.svelte';
|
||||
export { default as CallCard } from './CallCard.svelte';
|
||||
export { default as CalendarSection } from './CalendarSection.svelte';
|
||||
@@ -1,2 +1,4 @@
|
||||
export * from './shared/index.js';
|
||||
export * from './screener/index.js';
|
||||
export * from './portfolio/index.js';
|
||||
export * from './calls/index.js';
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export { default as AddHoldingForm } from './AddHoldingForm.svelte';
|
||||
export { default as AdviceTable } from './AdviceTable.svelte';
|
||||
export { default as AccountsTable } from './AccountsTable.svelte';
|
||||
+6
-170
@@ -1,170 +1,6 @@
|
||||
// ── UI type layer ─────────────────────────────────────────────────────────
|
||||
// Shared domain types are imported from the server's canonical model files
|
||||
// via the $types alias (→ server/types/). Only UI-specific types live here.
|
||||
//
|
||||
// All consumers should import from '$lib/types.js' as before — nothing changes
|
||||
// at the call site.
|
||||
|
||||
// ── Re-export shared domain types ────────────────────────────────────────
|
||||
export type {
|
||||
Signal,
|
||||
AssetType,
|
||||
ScoreMode,
|
||||
ScoreResult,
|
||||
AssetResult,
|
||||
ScreenerResult,
|
||||
} from '$types/asset.model.js';
|
||||
|
||||
export type {
|
||||
RateRegime,
|
||||
VolatilityRegime,
|
||||
Benchmarks,
|
||||
MarketContext,
|
||||
} from '$types/market.model.js';
|
||||
|
||||
export type { HoldingType, PortfolioHolding, PortfolioAdvice } from '$types/portfolio.model.js';
|
||||
|
||||
export type { TickerSnapshot, MarketCall } from '$types/calls.model.js';
|
||||
|
||||
export type { LLMAnalysis, CatalystStory, CalendarEvent } from '$types/finance.model.js';
|
||||
|
||||
// ── UI-only types (not on the server) ────────────────────────────────────
|
||||
|
||||
import type { AssetType } from '$types/asset.model.js';
|
||||
import type { LLMAnalysis } from '$types/finance.model.js';
|
||||
|
||||
/** Detailed display metrics rendered per asset row in the screener table. */
|
||||
export interface AssetDisplayMetrics {
|
||||
// ── Common ──────────────────────────────────────────────────────────
|
||||
Price?: string;
|
||||
|
||||
// ── Stock: classification ────────────────────────────────────────────
|
||||
Sector?: string;
|
||||
'Cap Tier'?: string; // Mega Cap / Large Cap / Mid Cap / Small Cap / Micro Cap
|
||||
Style?: string; // High Growth / Growth / Stable / Value / Turnaround / Declining
|
||||
|
||||
// ── Stock: valuation ─────────────────────────────────────────────────
|
||||
'P/E'?: string;
|
||||
PEG?: string;
|
||||
'P/B'?: string;
|
||||
|
||||
// ── Stock: quality ───────────────────────────────────────────────────
|
||||
'GrossM%'?: string; // gross margin — key for tech/software moat
|
||||
'ROE%'?: string;
|
||||
'OpMgn%'?: string;
|
||||
'NetMgn%'?: string;
|
||||
'FCF Yld%'?: string;
|
||||
'Div%'?: string;
|
||||
|
||||
// ── Stock: risk ───────────────────────────────────────────────────────
|
||||
'D/E'?: string;
|
||||
Quick?: string;
|
||||
Beta?: string;
|
||||
|
||||
// ── Stock: 52-week movement ───────────────────────────────────────────
|
||||
'52W Pos'?: string; // % position within the 52-week range
|
||||
'52W Chg'?: string; // total price return over last 52 weeks (signed %)
|
||||
'From High'?: string; // % below 52-week high (negative = drawdown)
|
||||
'From Low'?: string; // % above 52-week low (positive = recovery)
|
||||
|
||||
// ── Stock: analyst consensus ──────────────────────────────────────────
|
||||
Analyst?: string; // Strong Buy / Buy / Hold / Sell / Strong Sell
|
||||
'# Analysts'?: string;
|
||||
Target?: string; // analyst consensus price target
|
||||
Upside?: string; // % upside to analyst target (signed %)
|
||||
|
||||
// ── Stock: DCF intrinsic value ────────────────────────────────────────
|
||||
'DCF Value'?: string; // intrinsic value per share
|
||||
'DCF Safety'?: string; // margin of safety % (positive = undervalued)
|
||||
|
||||
// ── Stock: REIT-specific ──────────────────────────────────────────────
|
||||
'P/FFO'?: string;
|
||||
|
||||
// ── ETF ───────────────────────────────────────────────────────────────
|
||||
'Exp Ratio%'?: string;
|
||||
'Yield%'?: string;
|
||||
AUM?: string;
|
||||
'5Y Return%'?: string;
|
||||
|
||||
// ── Bond ──────────────────────────────────────────────────────────────
|
||||
'YTM%'?: string;
|
||||
Duration?: string;
|
||||
Rating?: string;
|
||||
|
||||
[key: string]: string | null | undefined;
|
||||
}
|
||||
|
||||
/** State object for the LLM analysis slide-over sidebar. */
|
||||
export interface SidebarState {
|
||||
open: boolean;
|
||||
loading: boolean;
|
||||
analysis: LLMAnalysis | null;
|
||||
type: AssetType | null;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
/** Transient state for inline row editing in the portfolio table. */
|
||||
export interface InlineEdit {
|
||||
ticker: string;
|
||||
shares: string;
|
||||
costBasis: string;
|
||||
type: string;
|
||||
source: string;
|
||||
}
|
||||
|
||||
// ── Portfolio component types ─────────────────────────────────────────────
|
||||
|
||||
import type { Signal } from '$types/asset.model.js';
|
||||
|
||||
/** A single row in the portfolio advice table. */
|
||||
export interface AdviceRow {
|
||||
ticker: string;
|
||||
type: string;
|
||||
source: string;
|
||||
shares: number;
|
||||
costBasis: number;
|
||||
currentPrice: string | null;
|
||||
marketValue: string | null;
|
||||
gainLossPct: string | null;
|
||||
signal: Signal | null;
|
||||
advice: string;
|
||||
reason: string;
|
||||
}
|
||||
|
||||
/** Form data for adding or updating a holding. */
|
||||
export interface HoldingFormData {
|
||||
ticker: string;
|
||||
shares: number;
|
||||
costBasis: number;
|
||||
type: 'stock' | 'etf' | 'bond' | 'crypto';
|
||||
source: string;
|
||||
}
|
||||
|
||||
interface SimpleFINAccount {
|
||||
name: string;
|
||||
type: string;
|
||||
org: string;
|
||||
balance: number;
|
||||
}
|
||||
|
||||
interface CategoryBreakdown {
|
||||
category: string;
|
||||
amount: number;
|
||||
pct: number;
|
||||
}
|
||||
|
||||
/** Personal finance summary from SimpleFIN. */
|
||||
export interface PersonalFinance {
|
||||
netWorth: number;
|
||||
totalAssets: number;
|
||||
totalLiabilities: number;
|
||||
totalCash: number;
|
||||
totalInvestments: number;
|
||||
totalIncome: number;
|
||||
totalSpend: number;
|
||||
cashPct: number;
|
||||
investPct: number;
|
||||
savingsRate: string | null;
|
||||
accounts: SimpleFINAccount[];
|
||||
categoryBreakdown: CategoryBreakdown[];
|
||||
}
|
||||
/**
|
||||
* Backward-compatibility shim.
|
||||
* Types have been split into lib/types/ subdirectory.
|
||||
* Existing '$lib/types.js' imports continue to work unchanged.
|
||||
*/
|
||||
export * from './types/index.js';
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './shared.js';
|
||||
export * from './ui.types.js';
|
||||
export * from './portfolio.types.js';
|
||||
@@ -0,0 +1,54 @@
|
||||
import type { Signal } from '$types/asset.model.js';
|
||||
|
||||
/** A single row in the portfolio advice table. */
|
||||
export interface AdviceRow {
|
||||
ticker: string;
|
||||
type: string;
|
||||
source: string;
|
||||
shares: number;
|
||||
costBasis: number;
|
||||
currentPrice: string | null;
|
||||
marketValue: string | null;
|
||||
gainLossPct: string | null;
|
||||
signal: Signal | null;
|
||||
advice: string;
|
||||
reason: string;
|
||||
}
|
||||
|
||||
/** Form data for adding or updating a holding. */
|
||||
export interface HoldingFormData {
|
||||
ticker: string;
|
||||
shares: number;
|
||||
costBasis: number;
|
||||
type: 'stock' | 'etf' | 'bond' | 'crypto';
|
||||
source: string;
|
||||
}
|
||||
|
||||
interface SimpleFINAccount {
|
||||
name: string;
|
||||
type: string;
|
||||
org: string;
|
||||
balance: number;
|
||||
}
|
||||
|
||||
interface CategoryBreakdown {
|
||||
category: string;
|
||||
amount: number;
|
||||
pct: number;
|
||||
}
|
||||
|
||||
/** Personal finance summary from SimpleFIN. */
|
||||
export interface PersonalFinance {
|
||||
netWorth: number;
|
||||
totalAssets: number;
|
||||
totalLiabilities: number;
|
||||
totalCash: number;
|
||||
totalInvestments: number;
|
||||
totalIncome: number;
|
||||
totalSpend: number;
|
||||
cashPct: number;
|
||||
investPct: number;
|
||||
savingsRate: string | null;
|
||||
accounts: SimpleFINAccount[];
|
||||
categoryBreakdown: CategoryBreakdown[];
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
export type {
|
||||
Signal,
|
||||
AssetType,
|
||||
ScoreMode,
|
||||
ScoreResult,
|
||||
AssetResult,
|
||||
ScreenerResult,
|
||||
} from '$types/asset.model.js';
|
||||
|
||||
export type {
|
||||
RateRegime,
|
||||
VolatilityRegime,
|
||||
Benchmarks,
|
||||
MarketContext,
|
||||
} from '$types/market.model.js';
|
||||
|
||||
export type { HoldingType, PortfolioHolding, PortfolioAdvice } from '$types/portfolio.model.js';
|
||||
|
||||
export type { TickerSnapshot, MarketCall } from '$types/calls.model.js';
|
||||
|
||||
export type { LLMAnalysis, CatalystStory, CalendarEvent } from '$types/finance.model.js';
|
||||
@@ -0,0 +1,81 @@
|
||||
import type { AssetType } from '$types/asset.model.js';
|
||||
import type { LLMAnalysis } from '$types/finance.model.js';
|
||||
|
||||
/** Detailed display metrics rendered per asset row in the screener table. */
|
||||
export interface AssetDisplayMetrics {
|
||||
// ── Common ──────────────────────────────────────────────────────────
|
||||
Price?: string;
|
||||
|
||||
// ── Stock: classification ────────────────────────────────────────────
|
||||
Sector?: string;
|
||||
'Cap Tier'?: string;
|
||||
Style?: string;
|
||||
|
||||
// ── Stock: valuation ─────────────────────────────────────────────────
|
||||
'P/E'?: string;
|
||||
PEG?: string;
|
||||
'P/B'?: string;
|
||||
|
||||
// ── Stock: quality ───────────────────────────────────────────────────
|
||||
'GrossM%'?: string;
|
||||
'ROE%'?: string;
|
||||
'OpMgn%'?: string;
|
||||
'NetMgn%'?: string;
|
||||
'FCF Yld%'?: string;
|
||||
'Div%'?: string;
|
||||
|
||||
// ── Stock: risk ───────────────────────────────────────────────────────
|
||||
'D/E'?: string;
|
||||
Quick?: string;
|
||||
Beta?: string;
|
||||
|
||||
// ── Stock: 52-week movement ───────────────────────────────────────────
|
||||
'52W Pos'?: string;
|
||||
'52W Chg'?: string;
|
||||
'From High'?: string;
|
||||
'From Low'?: string;
|
||||
|
||||
// ── Stock: analyst consensus ──────────────────────────────────────────
|
||||
Analyst?: string;
|
||||
'# Analysts'?: string;
|
||||
Target?: string;
|
||||
Upside?: string;
|
||||
|
||||
// ── Stock: DCF intrinsic value ────────────────────────────────────────
|
||||
'DCF Value'?: string;
|
||||
'DCF Safety'?: string;
|
||||
|
||||
// ── Stock: REIT-specific ──────────────────────────────────────────────
|
||||
'P/FFO'?: string;
|
||||
|
||||
// ── ETF ───────────────────────────────────────────────────────────────
|
||||
'Exp Ratio%'?: string;
|
||||
'Yield%'?: string;
|
||||
AUM?: string;
|
||||
'5Y Return%'?: string;
|
||||
|
||||
// ── Bond ──────────────────────────────────────────────────────────────
|
||||
'YTM%'?: string;
|
||||
Duration?: string;
|
||||
Rating?: string;
|
||||
|
||||
[key: string]: string | null | undefined;
|
||||
}
|
||||
|
||||
/** State object for the LLM analysis slide-over sidebar. */
|
||||
export interface SidebarState {
|
||||
open: boolean;
|
||||
loading: boolean;
|
||||
analysis: LLMAnalysis | null;
|
||||
type: AssetType | null;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
/** Transient state for inline row editing in the portfolio table. */
|
||||
export interface InlineEdit {
|
||||
ticker: string;
|
||||
shares: string;
|
||||
costBasis: string;
|
||||
type: string;
|
||||
source: string;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { createCall, deleteCall } from '$lib/api.js';
|
||||
import { invalidateAll } from '$app/navigation';
|
||||
import CallForm from '$lib/calls/CallForm.svelte';
|
||||
import CallCard from '$lib/calls/CallCard.svelte';
|
||||
import CalendarSection from '$lib/calls/CalendarSection.svelte';
|
||||
import CallForm from '$lib/components/calls/CallForm.svelte';
|
||||
import CallCard from '$lib/components/calls/CallCard.svelte';
|
||||
import CalendarSection from '$lib/components/calls/CalendarSection.svelte';
|
||||
import type { CalendarEvent } from '$lib/types.js';
|
||||
|
||||
interface MarketCall {
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
import { portfolioStore } from '$lib/stores/portfolio.store.svelte.js';
|
||||
import MarketContext from '$lib/components/shared/MarketContext.svelte';
|
||||
import Spinner from '$lib/components/shared/Spinner.svelte';
|
||||
import AddHoldingForm from '$lib/portfolio/AddHoldingForm.svelte';
|
||||
import AdviceTable from '$lib/portfolio/AdviceTable.svelte';
|
||||
import AccountsTable from '$lib/portfolio/AccountsTable.svelte';
|
||||
import AddHoldingForm from '$lib/components/portfolio/AddHoldingForm.svelte';
|
||||
import AdviceTable from '$lib/components/portfolio/AdviceTable.svelte';
|
||||
import AccountsTable from '$lib/components/portfolio/AccountsTable.svelte';
|
||||
|
||||
const p = portfolioStore;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user