Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/app/components/WalletModalTest.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { WalletModal } from "./WalletModal";

interface WalletModalTestProps {
isOpen: boolean;
onClose: () => void;
}

export function WalletModalTest({
isOpen,
onClose,
}: WalletModalTestProps) {
return <WalletModal isOpen={isOpen} onClose={onClose} />;
}
13 changes: 13 additions & 0 deletions frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -24,6 +25,18 @@ export default function RootLayout({
{children}
</ErrorBoundary>
</WalletProvider>
<Toaster
position="top-right"
toastOptions={{
duration: 4000,
style: {
background: 'rgba(255, 255, 255, 0.95)',
color: '#000',
border: '1px solid rgba(255, 255, 255, 0.2)',
backdropFilter: 'blur(10px)',
},
}}
/>
</body>
</html>
);
Expand Down
31 changes: 13 additions & 18 deletions frontend/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -40,13 +42,7 @@ export default function Home() {
isConnecting
} = useFreighter();

const [messages, setMessages] = useState<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?",
},
]);
const [messages, setMessages] = useState<ChatMessage[]>(initialMessages);
const [isTyping, setIsTyping] = useState(false);

const [allocations, setAllocations] = useState<AssetAllocation[]>(
Expand All @@ -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<GoalData>(() => ({
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);
Expand Down Expand Up @@ -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) {
Expand All @@ -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,
});
Expand Down Expand Up @@ -167,7 +162,7 @@ export default function Home() {
/>
</DashboardHeader>

<WalletModal
<WalletModalTest
isOpen={showInstallModal}
onClose={() => setShowInstallModal(false)}
/>
Expand Down
37 changes: 37 additions & 0 deletions frontend/src/config/mockData.ts
Original file line number Diff line number Diff line change
@@ -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',
},
];
7 changes: 7 additions & 0 deletions frontend/src/hooks/useFreighter.ts
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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;
}

Expand All @@ -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);
Expand All @@ -69,6 +75,7 @@ export function useFreighter() {
*/
const disconnect = useCallback(() => {
setPublicKey(null);
toast('Wallet disconnected');
}, [setPublicKey]);

return {
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/hooks/useNotifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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(
Expand All @@ -69,13 +71,15 @@ 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);
};

ws.onclose = () => {
console.log("[WS] Disconnected");
wsRef.current = null;
setIsConnected(false);
toast.error('Disconnected from notification service');

// Attempt reconnection with exponential backoff
if (reconnectAttemptsRef.current < maxReconnectAttempts) {
Expand Down
19 changes: 2 additions & 17 deletions frontend/src/utils/allocationParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

import type { AssetAllocation } from "./chartUtils";
import { defaultAllocations } from "../config/mockData";

// Color scheme matching the theme
const ALLOCATION_COLORS = {
Expand Down Expand Up @@ -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;
}
Binary file added temp.ps1
Binary file not shown.
Loading