diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 06db393..d44467d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,7 +11,8 @@ "lucide-react": "^0.577.0", "next": "16.1.6", "react": "19.2.3", - "react-dom": "19.2.3" + "react-dom": "19.2.3", + "react-hot-toast": "^2.6.0" }, "devDependencies": { "@types/node": "^20", @@ -2373,7 +2374,6 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -3496,6 +3496,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/goober": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.18.tgz", + "integrity": "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw==", + "license": "MIT", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -4847,6 +4856,23 @@ "react": "^19.2.3" } }, + "node_modules/react-hot-toast": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.6.0.tgz", + "integrity": "sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.3", + "goober": "^2.1.16" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index a6134b4..4d69a5d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,7 +12,8 @@ "lucide-react": "^0.577.0", "next": "16.1.6", "react": "19.2.3", - "react-dom": "19.2.3" + "react-dom": "19.2.3", + "react-hot-toast": "^2.6.0" }, "devDependencies": { "@types/node": "^20", diff --git a/frontend/src/app/components/WalletModalTest.tsx b/frontend/src/app/components/WalletModalTest.tsx new file mode 100644 index 0000000..7d9b653 --- /dev/null +++ b/frontend/src/app/components/WalletModalTest.tsx @@ -0,0 +1,13 @@ +import { WalletModal } from "./WalletModal"; + +interface WalletModalTestProps { + isOpen: boolean; + onClose: () => void; +} + +export function WalletModalTest({ + isOpen, + onClose, +}: WalletModalTestProps) { + return ; +} diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index fb297e6..a5f832d 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -2,6 +2,7 @@ import type { Metadata } from 'next'; import './globals.css'; import { WalletProvider } from './components/WalletContext'; import { ErrorBoundary } from './components/ErrorBoundary'; +import { Toaster } from 'react-hot-toast'; export const metadata: Metadata = { title: 'Smasage | AI Portfolio Manager', @@ -24,6 +25,18 @@ export default function RootLayout({ {children} + ); diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 6978cbf..9425136 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -26,8 +26,10 @@ import { GoalTrackerSkeleton, PortfolioChartSkeleton, } from "./components/SkeletonLoader"; -import { WalletModal } from "./components/WalletModal"; +import { WalletModalTest } from "./components/WalletModalTest"; import { ChatInterface, type ChatMessage } from "./components/ChatInterface"; +import { goalData, initialMessages } from "../config/mockData"; +import toast from 'react-hot-toast'; import { GoalTracker } from "./components/GoalTracker"; import { GlassPanel } from "./components/GlassPanel"; @@ -40,13 +42,7 @@ export default function Home() { isConnecting } = useFreighter(); - const [messages, setMessages] = useState([ - { - id: 1, - sender: "agent", - text: "Welcome to Smasage! 👋 I'm OpenClaw, your personal AI savings assistant natively built on Stellar. What financial goal can we crush today?", - }, - ]); + const [messages, setMessages] = useState(initialMessages); const [isTyping, setIsTyping] = useState(false); const [allocations, setAllocations] = useState( @@ -56,15 +52,6 @@ export default function Home() { const [wsConnected, setWsConnected] = useState(false); const [isLoading, setIsLoading] = useState(true); - // Goal data (Memoized to avoid unnecessary effect triggers) - const goalData = useMemo(() => ({ - currentBalance: 12450, - targetAmount: 18000, - targetDate: "2026-08-01", - monthlyContribution: 500, - expectedAPY: 8.5, - }), []); - // Calculate goal status and progress using useMemo to avoid cascading renders const { goalStatus, progress } = useMemo(() => { const result = evaluateGoalStatus(goalData); @@ -94,6 +81,13 @@ export default function Home() { }; setMessages((prev: ChatMessage[]) => [...prev, agentMsg]); + // Show toast for proactive messages + if (proactive) { + toast('💡 New suggestion from OpenClaw', { + duration: 5000, + }); + } + // Parse allocations if present const parsedAllocations = parseAllocationsFromMessage(text); if (parsedAllocations) { @@ -103,6 +97,7 @@ export default function Home() { }, onError: (error) => { console.error("[App] WebSocket error:", error); + toast.error('Failed to connect to notification service'); }, enabled: true, }); @@ -167,7 +162,7 @@ export default function Home() { /> - setShowInstallModal(false)} /> diff --git a/frontend/src/config/mockData.ts b/frontend/src/config/mockData.ts new file mode 100644 index 0000000..4074df2 --- /dev/null +++ b/frontend/src/config/mockData.ts @@ -0,0 +1,37 @@ +import type { ChatMessage } from '../app/components/ChatInterface'; +import type { AssetAllocation } from '../utils/chartUtils'; +import type { GoalData } from '../utils/goalProjection'; + +export const goalData: GoalData = { + currentBalance: 12450, + targetAmount: 18000, + targetDate: '2026-08-01', + monthlyContribution: 500, + expectedAPY: 8.5, +}; + +export const initialMessages: ChatMessage[] = [ + { + id: 1, + sender: 'agent', + text: 'Welcome to Smasage! 👋 I\'m OpenClaw, your personal AI savings assistant natively built on Stellar. What financial goal can we crush today?', + }, +]; + +export const defaultAllocations: AssetAllocation[] = [ + { + name: 'Blend Protocol Yield (USDC)', + percentage: 60, + color: '#8b5cf6', + }, + { + name: 'Soroswap LP (XLM/USDC)', + percentage: 30, + color: '#06b6d4', + }, + { + name: 'Stellar Anchored Gold (XAUT)', + percentage: 10, + color: '#f59e0b', + }, +]; diff --git a/frontend/src/hooks/useFreighter.ts b/frontend/src/hooks/useFreighter.ts index f211c4b..968bddd 100644 --- a/frontend/src/hooks/useFreighter.ts +++ b/frontend/src/hooks/useFreighter.ts @@ -1,5 +1,6 @@ import { useState, useCallback, useEffect } from "react"; import { useWallet } from "../app/components/WalletContext"; +import toast from 'react-hot-toast'; /** * Custom hook to manage Freighter wallet interactions. @@ -45,6 +46,9 @@ export function useFreighter() { if (!api) { setShowInstallModal(true); + toast.error('Freighter wallet not detected. Please install the extension and refresh the page.', { + duration: 6000, + }); return null; } @@ -55,9 +59,11 @@ export function useFreighter() { try { const key = await api.getPublicKey(); setPublicKey(key); + toast.success('Wallet connected successfully! 🎉'); return key; } catch (error) { console.error("[useFreighter] Connection failed:", error); + toast.error('Failed to connect wallet. Please try again.'); return null; } finally { setIsConnecting(false); @@ -69,6 +75,7 @@ export function useFreighter() { */ const disconnect = useCallback(() => { setPublicKey(null); + toast('Wallet disconnected'); }, [setPublicKey]); return { diff --git a/frontend/src/hooks/useNotifications.ts b/frontend/src/hooks/useNotifications.ts index 850fd95..652a7b8 100644 --- a/frontend/src/hooks/useNotifications.ts +++ b/frontend/src/hooks/useNotifications.ts @@ -11,6 +11,7 @@ import { WS_MAX_RECONNECT_DELAY_MS, } from "../config/constants"; import type { IncomingNotification, GoalPayload } from "../types/websocket"; +import toast from 'react-hot-toast'; // Re-export so existing imports from this module continue to work. export type { IncomingNotification } from "../types/websocket"; @@ -45,6 +46,7 @@ export function useNotifications(options: UseNotificationsOptions) { console.log("[WS] Connected"); reconnectAttemptsRef.current = 0; setIsConnected(true); + toast.success('Connected to notification service'); // Send initial registration ws.send( @@ -69,6 +71,7 @@ export function useNotifications(options: UseNotificationsOptions) { ws.onerror = (event) => { const error = new Error("WebSocket error"); console.error("[WS] Error:", error, event); + toast.error('Connection to notification service failed'); onError?.(error); }; @@ -76,6 +79,7 @@ export function useNotifications(options: UseNotificationsOptions) { console.log("[WS] Disconnected"); wsRef.current = null; setIsConnected(false); + toast.error('Disconnected from notification service'); // Attempt reconnection with exponential backoff if (reconnectAttemptsRef.current < maxReconnectAttempts) { diff --git a/frontend/src/utils/allocationParser.ts b/frontend/src/utils/allocationParser.ts index 7ebffd4..86f6776 100644 --- a/frontend/src/utils/allocationParser.ts +++ b/frontend/src/utils/allocationParser.ts @@ -4,6 +4,7 @@ */ import type { AssetAllocation } from "./chartUtils"; +import { defaultAllocations } from "../config/mockData"; // Color scheme matching the theme const ALLOCATION_COLORS = { @@ -93,21 +94,5 @@ function getColorForAsset(assetName: string): string { * Get default allocations */ export function getDefaultAllocations(): AssetAllocation[] { - return [ - { - name: "Blend Protocol Yield (USDC)", - percentage: 60, - color: ALLOCATION_COLORS.blend, - }, - { - name: "Soroswap LP (XLM/USDC)", - percentage: 30, - color: ALLOCATION_COLORS.soroswap, - }, - { - name: "Stellar Anchored Gold (XAUT)", - percentage: 10, - color: ALLOCATION_COLORS.gold, - }, - ]; + return defaultAllocations; } diff --git a/temp.ps1 b/temp.ps1 new file mode 100644 index 0000000..69e4edc Binary files /dev/null and b/temp.ps1 differ