Skip to content

Commit 917d6c5

Browse files
author
DogInfantry
committed
feat: add interactive Next.js dashboard with Recharts visualizations
- 6-tab dashboard: Overview, Yield Curve, Market Regime, IPO Markets, M&A Screening, Sovereign Risk - Recharts-powered charts: line, area, bar, scatter, composed with custom dark theme tooltips - Reads all 14 CSVs via /api/data route (yield curve, regime, IPO events, M&A screening, sovereign risk, stress tests, FX) - KPI stat cards, data tables, regime detection, term structure snapshot - DM Mono + Syne + Space Grotesk typography on deep navy dark theme - Zero API keys required — all data from existing pipeline outputs - Vercel-ready with vercel.json config
1 parent a852b27 commit 917d6c5

44 files changed

Lines changed: 16748 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

dashboard/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules/
2+
.next/
3+
.env*.local

dashboard/AGENTS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<!-- BEGIN:nextjs-agent-rules -->
2+
# This is NOT the Next.js you know
3+
4+
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
5+
<!-- END:nextjs-agent-rules -->

dashboard/CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@AGENTS.md

dashboard/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
2+
3+
## Getting Started
4+
5+
First, run the development server:
6+
7+
```bash
8+
npm run dev
9+
# or
10+
yarn dev
11+
# or
12+
pnpm dev
13+
# or
14+
bun dev
15+
```
16+
17+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18+
19+
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20+
21+
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
22+
23+
## Learn More
24+
25+
To learn more about Next.js, take a look at the following resources:
26+
27+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29+
30+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
31+
32+
## Deploy on Vercel
33+
34+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35+
36+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

dashboard/app/api/data/route.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { NextRequest, NextResponse } from 'next/server';
2+
import fs from 'fs';
3+
import path from 'path';
4+
5+
function parseCSV(content: string) {
6+
const lines = content.trim().split('\n');
7+
if (lines.length < 2) return [];
8+
const headers = lines[0].split(',').map(h => h.trim().replace(/^"|"$/g, ''));
9+
return lines.slice(1).map(line => {
10+
const values: string[] = [];
11+
let current = '';
12+
let inQuotes = false;
13+
for (const char of line) {
14+
if (char === '"') { inQuotes = !inQuotes; }
15+
else if (char === ',' && !inQuotes) { values.push(current); current = ''; }
16+
else { current += char; }
17+
}
18+
values.push(current);
19+
const row: Record<string, string | number | null> = {};
20+
headers.forEach((h, i) => {
21+
const val = (values[i] || '').trim().replace(/^"|"$/g, '');
22+
const num = Number(val);
23+
row[h] = val === '' ? null : isNaN(num) ? val : num;
24+
});
25+
return row;
26+
});
27+
}
28+
29+
export async function GET(req: NextRequest) {
30+
const { searchParams } = new URL(req.url);
31+
const file = searchParams.get('file');
32+
if (!file || file.includes('..')) {
33+
return NextResponse.json({ error: 'Invalid file' }, { status: 400 });
34+
}
35+
const dataDir = path.join(process.cwd(), 'public', 'data');
36+
const filePath = path.join(dataDir, file);
37+
try {
38+
const content = fs.readFileSync(filePath, 'utf-8');
39+
const data = parseCSV(content);
40+
return NextResponse.json(data);
41+
} catch {
42+
return NextResponse.json({ error: 'File not found' }, { status: 404 });
43+
}
44+
}

dashboard/app/favicon.ico

25.3 KB
Binary file not shown.

dashboard/app/globals.css

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
@import url('https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300&family=Syne:wght@400;500;600;700;800&family=Space+Grotesk:wght@300;400;500;600;700&display=swap');
2+
@import "tailwindcss";
3+
4+
:root {
5+
--bg-primary: #050A0F;
6+
--bg-secondary: #080F18;
7+
--bg-card: #0B1420;
8+
--bg-card-hover: #0F1C2E;
9+
--border: #1A2A3A;
10+
--border-bright: #1E3A5F;
11+
--text-primary: #E2EAF4;
12+
--text-secondary: #7A9BBE;
13+
--text-muted: #3D5A7A;
14+
--accent-blue: #0EA5E9;
15+
--accent-cyan: #06B6D4;
16+
--accent-green: #10B981;
17+
--accent-amber: #F59E0B;
18+
--accent-red: #EF4444;
19+
--accent-purple: #8B5CF6;
20+
--accent-orange: #F97316;
21+
--glow-blue: rgba(14, 165, 233, 0.15);
22+
--glow-green: rgba(16, 185, 129, 0.12);
23+
--glow-red: rgba(239, 68, 68, 0.12);
24+
}
25+
26+
* { box-sizing: border-box; }
27+
html { scroll-behavior: smooth; }
28+
29+
body {
30+
background-color: var(--bg-primary);
31+
color: var(--text-primary);
32+
font-family: 'Space Grotesk', sans-serif;
33+
font-size: 14px;
34+
line-height: 1.5;
35+
-webkit-font-smoothing: antialiased;
36+
}
37+
38+
h1, h2, h3, h4, h5 { font-family: 'Syne', sans-serif; letter-spacing: -0.02em; }
39+
code, pre, .mono { font-family: 'DM Mono', monospace; }
40+
41+
::-webkit-scrollbar { width: 4px; height: 4px; }
42+
::-webkit-scrollbar-track { background: var(--bg-secondary); }
43+
::-webkit-scrollbar-thumb { background: var(--border-bright); border-radius: 2px; }
44+
45+
.card {
46+
background: var(--bg-card);
47+
border: 1px solid var(--border);
48+
border-radius: 8px;
49+
transition: border-color 0.2s ease, background 0.2s ease;
50+
}
51+
.card:hover { border-color: var(--border-bright); background: var(--bg-card-hover); }
52+
53+
.stat-value {
54+
font-family: 'DM Mono', monospace;
55+
font-size: 1.5rem;
56+
font-weight: 500;
57+
letter-spacing: -0.03em;
58+
}
59+
60+
@keyframes fadeIn {
61+
from { opacity: 0; transform: translateY(8px); }
62+
to { opacity: 1; transform: translateY(0); }
63+
}
64+
.animate-in { animation: fadeIn 0.4s ease forwards; }
65+
66+
@keyframes pulse-dot {
67+
0%, 100% { opacity: 1; }
68+
50% { opacity: 0.3; }
69+
}
70+
.pulse-dot { animation: pulse-dot 2s ease-in-out infinite; }
71+
72+
.recharts-cartesian-grid-horizontal line,
73+
.recharts-cartesian-grid-vertical line { stroke: #1A2A3A !important; }
74+
.recharts-tooltip-wrapper { outline: none !important; }
75+
76+
.nav-item {
77+
font-family: 'DM Mono', monospace;
78+
font-size: 0.72rem;
79+
letter-spacing: 0.06em;
80+
text-transform: uppercase;
81+
transition: all 0.2s ease;
82+
}

dashboard/app/layout.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { Metadata } from "next";
2+
import "./globals.css";
3+
4+
export const metadata: Metadata = {
5+
title: "Capital Markets Intelligence | DogInfantry",
6+
description: "Production-grade capital markets intelligence platform — IPO event studies, sovereign risk scoring, M&A screening, and yield curve decomposition.",
7+
keywords: "capital markets, IPO, M&A, sovereign risk, yield curve, quantitative finance",
8+
};
9+
10+
export default function RootLayout({
11+
children,
12+
}: {
13+
children: React.ReactNode;
14+
}) {
15+
return (
16+
<html lang="en">
17+
<head>
18+
<link rel="preconnect" href="https://fonts.googleapis.com" />
19+
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
20+
</head>
21+
<body>{children}</body>
22+
</html>
23+
);
24+
}

0 commit comments

Comments
 (0)