diff --git a/apps/client/src/app/components/ai-chat/ai-chat.component.html b/apps/client/src/app/components/ai-chat/ai-chat.component.html
index c80f28ea9..67a03a4dc 100644
--- a/apps/client/src/app/components/ai-chat/ai-chat.component.html
+++ b/apps/client/src/app/components/ai-chat/ai-chat.component.html
@@ -29,6 +29,20 @@
{{ successBanner }}
}
+
+ @if (showSeedBanner && !isSeeding) {
+
+
Your portfolio is empty. Load demo data to try the AI?
+
+
+
+
+
+ }
+ @if (isSeeding) {
+ ⏳ Loading demo portfolio…
+ }
+
@for (msg of messages; track $index) {
diff --git a/apps/client/src/app/components/ai-chat/ai-chat.component.scss b/apps/client/src/app/components/ai-chat/ai-chat.component.scss
index 8b8c76104..a619c41bb 100644
--- a/apps/client/src/app/components/ai-chat/ai-chat.component.scss
+++ b/apps/client/src/app/components/ai-chat/ai-chat.component.scss
@@ -159,6 +159,51 @@
color: #a7f3d0;
}
}
+
+ &--seed {
+ background: #eff6ff;
+ color: #1e40af;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ font-weight: 500;
+
+ :host-context(.theme-dark) & {
+ background: #1e3a5f;
+ color: #bfdbfe;
+ }
+ }
+
+ &__actions {
+ display: flex;
+ gap: 0.4rem;
+ flex-shrink: 0;
+ }
+
+ &__btn {
+ border: none;
+ border-radius: 6px;
+ padding: 0.25rem 0.65rem;
+ font-size: 0.8rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition: opacity 0.15s;
+
+ &:hover { opacity: 0.85; }
+
+ &--primary {
+ background: #2563eb;
+ color: #fff;
+ }
+
+ &--dismiss {
+ background: transparent;
+ color: inherit;
+ opacity: 0.6;
+ }
+ }
}
@keyframes slideDown {
diff --git a/apps/client/src/app/components/ai-chat/ai-chat.component.ts b/apps/client/src/app/components/ai-chat/ai-chat.component.ts
index 97ba793f1..4555f5a5d 100644
--- a/apps/client/src/app/components/ai-chat/ai-chat.component.ts
+++ b/apps/client/src/app/components/ai-chat/ai-chat.component.ts
@@ -50,6 +50,8 @@ export class GfAiChatComponent implements OnDestroy {
public inputValue = '';
public messages: ChatMessage[] = [];
public successBanner = '';
+ public showSeedBanner = false;
+ public isSeeding = false;
// Write confirmation state
private pendingWrite: Record | null = null;
@@ -57,6 +59,7 @@ export class GfAiChatComponent implements OnDestroy {
private readonly AGENT_URL: string;
private readonly FEEDBACK_URL: string;
+ private readonly SEED_URL: string;
public constructor(
private changeDetectorRef: ChangeDetectorRef,
@@ -67,6 +70,7 @@ export class GfAiChatComponent implements OnDestroy {
const base = (environment.agentUrl ?? '/agent').replace(/\/$/, '');
this.AGENT_URL = `${base}/chat`;
this.FEEDBACK_URL = `${base}/feedback`;
+ this.SEED_URL = `${base}/seed`;
}
public ngOnDestroy() {}
@@ -166,6 +170,15 @@ export class GfAiChatComponent implements OnDestroy {
this.awaitingConfirmation = data.awaiting_confirmation;
this.pendingWrite = data.pending_write;
+ // Detect an empty portfolio and offer to seed demo data
+ const emptyPortfolioHints = ['0 holdings', '0 positions', 'no holdings', 'no positions', 'empty portfolio', 'no transactions', '0.00 (0.0%)'];
+ const isEmptyPortfolio = emptyPortfolioHints.some((hint) =>
+ data.response.toLowerCase().includes(hint)
+ );
+ if (isEmptyPortfolio && !this.showSeedBanner) {
+ this.showSeedBanner = true;
+ }
+
if (isWriteSuccess) {
this.successBanner = '✅ Transaction recorded successfully';
setTimeout(() => {
@@ -192,6 +205,39 @@ export class GfAiChatComponent implements OnDestroy {
});
}
+ public seedPortfolio(): void {
+ this.isSeeding = true;
+ this.showSeedBanner = false;
+ this.changeDetectorRef.markForCheck();
+
+ const body: { bearer_token?: string } = {};
+ const userToken = this.tokenStorageService.getToken();
+ if (userToken) {
+ body.bearer_token = userToken;
+ }
+
+ this.http.post<{ success: boolean; message: string }>(this.SEED_URL, body).subscribe({
+ next: (data) => {
+ this.isSeeding = false;
+ if (data.success) {
+ this.messages.push({
+ role: 'assistant',
+ content: `🌱 **Demo portfolio loaded!** I've added 18 transactions across AAPL, MSFT, NVDA, GOOGL, AMZN, and VTI spanning 2021–2024. Try asking "how is my portfolio doing?" to see your analysis.`
+ });
+ } else {
+ this.messages.push({ role: 'assistant', content: '⚠️ Could not load demo data. Please try again.' });
+ }
+ this.changeDetectorRef.markForCheck();
+ this.scrollToBottom();
+ },
+ error: () => {
+ this.isSeeding = false;
+ this.messages.push({ role: 'assistant', content: '⚠️ Could not reach the seeding endpoint. Make sure the agent is running.' });
+ this.changeDetectorRef.markForCheck();
+ }
+ });
+ }
+
public giveFeedback(
msgIndex: number,
rating: 1 | -1