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
5 changes: 3 additions & 2 deletions src/app/api/portfolio/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ export async function GET(request: NextRequest) {
const portfolioPath =
process.env.NEUROWEALTH_PORTFOLIO_PATH ?? "/portfolio/overview";

if (scenario === "empty") {
return NextResponse.json(buildScenarioPayload("empty"), {
// Handle all sandbox scenarios
if (scenario !== "live") {
return NextResponse.json(buildScenarioPayload(scenario), {
headers: {
"Cache-Control": "no-store",
"x-neurowealth-source": "demo",
Expand Down
117 changes: 97 additions & 20 deletions src/app/dashboard/history/page.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,90 @@
"use client";

import { useState } from "react";
import { Clock } from "lucide-react";
import { useState, useEffect } from "react";
import { useSearchParams } from "next/navigation";
import { Clock, AlertTriangle, Loader2 } from "lucide-react";
import { EmptyState } from "@/components/ui/EmptyState";
import { TableSkeleton } from "@/components/ui/Skeleton";
import { useSandbox } from "@/contexts/SandboxContext";

export default function HistoryPage() {
const [showEmpty, setShowEmpty] = useState(true);
const searchParams = useSearchParams();
const { getCurrentScenario, isSandboxMode } = useSandbox();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);

const scenario = getCurrentScenario("history");

useEffect(() => {
if (scenario === "loading") {
setLoading(true);
const timer = setTimeout(() => setLoading(false), 3000);
return () => clearTimeout(timer);
} else if (scenario === "timeout") {
setLoading(true);
const timer = setTimeout(() => {
setLoading(false);
setError("Request timed out. Please try again.");
}, 5000);
return () => clearTimeout(timer);
} else if (scenario === "partial-failure") {
setError("Some transaction data could not be loaded. Showing partial results.");
} else {
setLoading(false);
setError(null);
}
}, [scenario]);

if (loading) {
return (
<div className="px-6 pt-8">
<div className="flex items-center justify-between pb-4">
<h1 className="text-2xl font-bold text-slate-100">History</h1>
{isSandboxMode && (
<span className="px-2 py-1 bg-green-100 text-green-800 rounded-full text-xs font-medium">
Sandbox: {scenario}
</span>
)}
</div>
<TableSkeleton rows={6} cols={5} />
</div>
);
}

if (showEmpty) {
if (error) {
return (
<div className="px-6 pt-8">
<div className="flex items-center justify-between pb-4">
<h1 className="text-2xl font-bold text-slate-100">History</h1>
{isSandboxMode && (
<span className="px-2 py-1 bg-green-100 text-green-800 rounded-full text-xs font-medium">
Sandbox: {scenario}
</span>
)}
</div>
<div className="bg-red-50 border border-red-200 rounded-lg p-6">
<div className="flex items-center space-x-3">
<AlertTriangle className="text-red-600" size={20} />
<div>
<h3 className="text-red-800 font-medium">Error loading history</h3>
<p className="text-red-600 text-sm mt-1">{error}</p>
</div>
</div>
</div>
</div>
);
}

if (scenario === "empty") {
return (
<div className="min-h-[60vh] flex flex-col">
<div className="flex items-center justify-between px-6 pt-8 pb-4">
<h1 className="text-2xl font-bold text-slate-100">History</h1>
<button
onClick={() => {
setLoading(true);
setTimeout(() => { setLoading(false); setShowEmpty(false); }, 1000);
}}
className="text-xs text-slate-500 hover:text-slate-300 transition-colors"
>
Mock: show data
</button>
{isSandboxMode && (
<span className="px-2 py-1 bg-green-100 text-green-800 rounded-full text-xs font-medium">
Sandbox: {scenario}
</span>
)}
</div>
<div className="flex-1 flex items-center justify-center">
<EmptyState
Expand All @@ -52,14 +103,40 @@ export default function HistoryPage() {
<div className="px-6 pt-8">
<div className="flex items-center justify-between pb-4">
<h1 className="text-2xl font-bold text-slate-100">History</h1>
<button
onClick={() => setShowEmpty(true)}
className="text-xs text-slate-500 hover:text-slate-300 transition-colors"
>
Mock: show empty
</button>
{isSandboxMode && (
<span className="px-2 py-1 bg-green-100 text-green-800 rounded-full text-xs font-medium">
Sandbox: {scenario}
</span>
)}
</div>
<div className="bg-white rounded-lg shadow-sm">
<div className="p-6">
<h3 className="text-lg font-medium text-gray-900 mb-4">Recent Transactions</h3>
<div className="space-y-4">
<div className="flex items-center justify-between py-3 border-b">
<div>
<p className="font-medium text-gray-900">Deposit</p>
<p className="text-sm text-gray-500">Mar 23, 2026 • 2:30 PM</p>
</div>
<span className="text-green-600 font-medium">+$1,000.00</span>
</div>
<div className="flex items-center justify-between py-3 border-b">
<div>
<p className="font-medium text-gray-900">Yield Payment</p>
<p className="text-sm text-gray-500">Mar 22, 2026 • 11:45 AM</p>
</div>
<span className="text-green-600 font-medium">+$12.50</span>
</div>
<div className="flex items-center justify-between py-3">
<div>
<p className="font-medium text-gray-900">Rebalance</p>
<p className="text-sm text-gray-500">Mar 21, 2026 • 9:15 AM</p>
</div>
<span className="text-gray-600 font-medium">Reallocation</span>
</div>
</div>
</div>
</div>
<p className="text-slate-400">Transaction history content goes here.</p>
</div>
);
}
Loading
Loading