{isExpanded && (
diff --git a/app/frontend/lib/api.ts b/app/frontend/lib/api.ts
index 3d4b80f..66d155e 100644
--- a/app/frontend/lib/api.ts
+++ b/app/frontend/lib/api.ts
@@ -443,6 +443,8 @@ export const dividendsApi = {
export interface ProfileUpdate {
portfolioSlug?: string | null
preferredCurrency?: string
+ sharePortfolio?: boolean
+ shareRadar?: boolean
}
// ============================================================================
@@ -483,6 +485,8 @@ export const profileApi = {
const body: Record = {}
if ('portfolioSlug' in update) body.portfolio_slug = update.portfolioSlug
if ('preferredCurrency' in update) body.preferred_currency = update.preferredCurrency
+ if ('sharePortfolio' in update) body.share_portfolio = update.sharePortfolio
+ if ('shareRadar' in update) body.share_radar = update.shareRadar
return apiFetch('/profile', {
method: 'PATCH',
body: JSON.stringify(body),
diff --git a/app/frontend/lib/pulse.ts b/app/frontend/lib/pulse.ts
index ca4d53e..2acef9e 100644
--- a/app/frontend/lib/pulse.ts
+++ b/app/frontend/lib/pulse.ts
@@ -14,3 +14,11 @@ export function pulsePortfolioUrl(slug: string): string {
export function pulsePortfolioDisplayUrl(slug: string): string {
return `${PULSE_URL.replace(/^https?:\/\//, '')}/p/${slug}`
}
+
+export function pulseRadarUrl(slug: string): string {
+ return `${PULSE_URL}/r/${slug}`
+}
+
+export function pulseRadarDisplayUrl(slug: string): string {
+ return `${PULSE_URL.replace(/^https?:\/\//, '')}/r/${slug}`
+}
diff --git a/app/frontend/lib/sectors.ts b/app/frontend/lib/sectors.ts
new file mode 100644
index 0000000..f1d4d47
--- /dev/null
+++ b/app/frontend/lib/sectors.ts
@@ -0,0 +1,6 @@
+import type { TFunction } from 'i18next'
+
+export function translateSector(t: TFunction, sector: string | null | undefined): string {
+ if (!sector) return ''
+ return t(`sectors.${sector}`, { defaultValue: sector })
+}
diff --git a/app/frontend/locales/en.ts b/app/frontend/locales/en.ts
index 289e6f4..d3863f3 100644
--- a/app/frontend/locales/en.ts
+++ b/app/frontend/locales/en.ts
@@ -174,6 +174,8 @@ const en = {
failedToLoadRadar: 'Failed to load radar',
failedToRemoveStock: 'Failed to remove stock',
dividendCalendar: 'Dividend Calendar',
+ shareOnPulse: 'Share on Pulse',
+ shareOnPulseHint: 'Share your radar on Pulse — opt in from Settings',
switchToCardView: 'Switch to card view',
switchToCompactView: 'Switch to compact view',
addToCart: 'Add to Cart',
@@ -335,7 +337,8 @@ const en = {
// AI Insights (shared between radar and portfolio)
insights: {
- title: 'AI Portfolio Insights',
+ portfolioTitle: 'AI Portfolio Insights',
+ radarTitle: 'AI Radar Insights',
buyingOpportunities: 'Buying Opportunities',
coverageGaps: 'Dividend Coverage Gaps',
riskFlags: 'Risk Flags',
@@ -381,11 +384,18 @@ const en = {
settings: {
title: 'Settings',
portfolioSharing: 'Share on Pulse',
- sharingDescription: 'Pulse is the Quantic community for sharing portfolios. Pick a public name to opt in — leave it empty to stay private.',
+ sharingDescription: 'Pulse is the Quantic community for sharing portfolios and radars. Pick a public name to opt in — leave it empty to stay private.',
portfolioSlug: 'Public name',
slugPlaceholder: 'my-portfolio',
publicUrl: 'Public URL:',
failedToUpdate: 'Failed to update',
+ sharing: {
+ whatToShare: 'What to share publicly',
+ portfolio: 'Portfolio',
+ portfolioDescription: 'Your holdings, allocations, and total value (in your display currency).',
+ radar: 'Radar',
+ radarDescription: 'Your watchlist with target prices. No quantities or holdings — just what you’re tracking and at what price.',
+ },
displayCurrency: 'Display Currency',
displayCurrencyDescription: 'Choose the currency used to sum multi-currency totals. Per-stock prices stay in their listing currency.',
telegram: {
@@ -552,6 +562,30 @@ const en = {
body: "You've used your {{limit}} free AI requests today. AI calls cost real money \u2014 we're offering them free for now. Try again tomorrow.",
},
},
+
+ // GICS / Yahoo Finance stock sectors. Looked up by raw provider name
+ // (see translateSector in lib/sectors.ts) \u2014 unknown names pass through
+ // verbatim so a new sector never blanks the UI.
+ sectors: {
+ 'Basic Materials': 'Basic Materials',
+ 'Communication Services': 'Communication Services',
+ 'Consumer Cyclical': 'Consumer Cyclical',
+ 'Consumer Defensive': 'Consumer Defensive',
+ 'Consumer Discretionary': 'Consumer Discretionary',
+ 'Consumer Staples': 'Consumer Staples',
+ Energy: 'Energy',
+ 'Financial Services': 'Financial Services',
+ Financials: 'Financials',
+ 'Health Care': 'Health Care',
+ Healthcare: 'Healthcare',
+ Industrials: 'Industrials',
+ 'Information Technology': 'Information Technology',
+ Materials: 'Materials',
+ 'Real Estate': 'Real Estate',
+ Technology: 'Technology',
+ Utilities: 'Utilities',
+ Unknown: 'Unknown',
+ },
} as const
export default en
diff --git a/app/frontend/locales/es.ts b/app/frontend/locales/es.ts
index e9f322e..e998f09 100644
--- a/app/frontend/locales/es.ts
+++ b/app/frontend/locales/es.ts
@@ -174,6 +174,8 @@ const es = {
failedToLoadRadar: 'Error al cargar el radar',
failedToRemoveStock: 'Error al eliminar acci\u00f3n',
dividendCalendar: 'Calendario de Dividendos',
+ shareOnPulse: 'Compartir en Pulse',
+ shareOnPulseHint: 'Comparte tu radar en Pulse \u2014 act\u00edvalo en Ajustes',
switchToCardView: 'Cambiar a vista de tarjetas',
switchToCompactView: 'Cambiar a vista compacta',
addToCart: 'A\u00f1adir al Carrito',
@@ -334,7 +336,8 @@ const es = {
// AI Insights
insights: {
- title: 'An\u00e1lisis IA de Cartera',
+ portfolioTitle: 'An\u00e1lisis IA de Cartera',
+ radarTitle: 'An\u00e1lisis IA del Radar',
buyingOpportunities: 'Oportunidades de Compra',
coverageGaps: 'Meses sin Cobertura de Dividendos',
riskFlags: 'Alertas de Riesgo',
@@ -380,10 +383,17 @@ const es = {
settings: {
title: 'Ajustes',
portfolioSharing: 'Compartir en Pulse',
- sharingDescription: 'Pulse es la comunidad de Quantic para compartir carteras. Elige un nombre p\u00fablico para participar \u2014 d\u00e9jalo vac\u00edo para no participar.',
+ sharingDescription: 'Pulse es la comunidad de Quantic para compartir carteras y radares. Elige un nombre p\u00fablico para participar \u2014 d\u00e9jalo vac\u00edo para no participar.',
portfolioSlug: 'Nombre p\u00fablico',
slugPlaceholder: 'mi-cartera',
publicUrl: 'URL p\u00fablica:',
+ sharing: {
+ whatToShare: 'Qu\u00e9 compartir p\u00fablicamente',
+ portfolio: 'Cartera',
+ portfolioDescription: 'Tus posiciones, asignaciones y valor total (en tu moneda de visualizaci\u00f3n).',
+ radar: 'Radar',
+ radarDescription: 'Tu lista de seguimiento con precios objetivo. Sin cantidades ni posiciones \u2014 solo qu\u00e9 est\u00e1s siguiendo y a qu\u00e9 precio.',
+ },
failedToUpdate: 'Error al actualizar',
displayCurrency: 'Moneda de visualizaci\u00f3n',
displayCurrencyDescription: 'Elige la moneda que se usa para sumar totales multi-divisa. Los precios por acci\u00f3n se mantienen en su moneda de cotizaci\u00f3n.',
@@ -551,6 +561,30 @@ const es = {
body: 'Has usado tus {{limit}} solicitudes gratuitas de IA hoy. Las llamadas a la IA cuestan dinero real — las ofrecemos gratis por ahora. Inténtalo de nuevo mañana.',
},
},
+
+ // GICS / Yahoo Finance stock sectors. Looked up by raw provider name
+ // (see translateSector in lib/sectors.ts) — unknown names pass through
+ // verbatim so a new sector never blanks the UI.
+ sectors: {
+ 'Basic Materials': 'Materiales Básicos',
+ 'Communication Services': 'Servicios de Comunicación',
+ 'Consumer Cyclical': 'Consumo Cíclico',
+ 'Consumer Defensive': 'Consumo Defensivo',
+ 'Consumer Discretionary': 'Consumo Discrecional',
+ 'Consumer Staples': 'Consumo Básico',
+ Energy: 'Energía',
+ 'Financial Services': 'Servicios Financieros',
+ Financials: 'Finanzas',
+ 'Health Care': 'Salud',
+ Healthcare: 'Salud',
+ Industrials: 'Industria',
+ 'Information Technology': 'Tecnología de la Información',
+ Materials: 'Materiales',
+ 'Real Estate': 'Inmobiliario',
+ Technology: 'Tecnología',
+ Utilities: 'Servicios Públicos',
+ Unknown: 'Desconocido',
+ },
} as const
export default es
diff --git a/app/frontend/pages/PortfolioPage.tsx b/app/frontend/pages/PortfolioPage.tsx
index c3c39b8..daec30b 100644
--- a/app/frontend/pages/PortfolioPage.tsx
+++ b/app/frontend/pages/PortfolioPage.tsx
@@ -166,7 +166,7 @@ export function PortfolioPage() {
{t('portfolio.title')}
- {holdings.length > 0 && }
+ {holdings.length > 0 && }
+
+ {/* What to share — only meaningful when a slug is set. Defaults:
+ portfolio ON (backwards-compat with the current behaviour),
+ radar OFF (opt-in for the new surface). */}
+ {profile?.portfolioSlug && (
+