Skip to content
Open
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: 2 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# Copy this file to .env and provide your private OpenAI API key.
# Copy this file to server/.env (or export the variables) before running the Node service.
OPENAI_API_KEY=replace-with-your-openai-api-key
# Optionally override the Flask secret key for session security
# FLASK_SECRET_KEY=change-me
PORT=5001
10 changes: 8 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
__pycache__/
*.pyc
node_modules/
client/node_modules/
server/node_modules/
dist/
client/dist/
server/dist/
.env
server/.env
.DS_Store
116 changes: 77 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,69 +1,107 @@
# Insight Navigator
# Insight Nexus CRM

Insight Navigator is a local-first business intelligence assistant that combines
predictive analytics, risk management, and guided advisory conversations through
your ChatGPT connection.
Insight Nexus CRM is an immersive, local-first Vite + React command center for predictive revenue intelligence.
It fuses data management, forecasting, risk analysis, AI advisory, and deep comparison workflows into one cinematic
experience powered by your private OpenAI key.

## Architecture

```
root
├── client # Vite + React + Tailwind front-end (TypeScript)
└── server # Node/Express API broker that talks to OpenAI Responses
```

The UI runs on Vite (port 5173) and proxies API calls to the Node service (port 5001 by default). All OpenAI traffic
is handled server-side so your key never touches the browser.

## Features

- **Business predictor module** that transforms your metrics into growth outlooks
and actionable recommendations.
- **Risk management insights** with mitigation strategies aligned to your stated
tolerance level.
- **Dynamic SWOT summaries** and professional-grade data visualizations powered by Chart.js.
- **Advisor chat workspace** for continuing strategic discussions and next steps.
- **Responsive, multi-page Flask interface** with polished styling.
- **Data fabric ops** – upload CRM CSV/JSON/TXT intelligence, track freshness, and surface AI-generated coverage maps.
- **Prediction studio** – craft multi-dimensional business scenarios, attach documents, and receive structured forecasts,
risk posture, CRM plays, and AI advisory in one response.
- **Risk command** – monitor Atlas-generated risk matrices, mitigation sprints, and telemetry streams in a premium glassmorphism UI.
- **AI advisor** – chat with Atlas, the revenue copilot, for executive-ready guidance that remembers context across turns.
- **Collaboration hub** – upload two intelligence files for deep delta analysis with suggested actions, automation plays, and executive summaries.

## Prerequisites

1. Python 3.10+
2. An OpenAI API key with access to the ChatGPT Responses API.
- Node.js 18+
- npm 9+
- An OpenAI API key with access to the Responses API (e.g., `gpt-4.1-mini`)

## Setup

### Windows quick-start

If you're on Windows 11, the repository includes helper scripts:

1. Double-click `install.bat` to verify Node/npm are available and install server/client dependencies. The installer skips
modules that are already present.
2. Copy `server/.env.example` to `server/.env` and set `OPENAI_API_KEY=<your key>`.
3. Double-click `run.bat` to launch both the Node API broker and Vite client in separate terminals.

## Quick start
### Manual setup

1. Clone the repository and move into the project directory.
2. Run the auto-installer (skips packages that are already installed):
1. **Install dependencies**

```bash
python install.py
cd server && npm install
cd ../client && npm install
```

On Windows you can double-click `install.bat` or run it from a terminal:
2. **Configure environment variables**

```bat
install.bat
```bash
cp server/.env.example server/.env
# edit server/.env and set OPENAI_API_KEY=<your key>
```

3. Create a `.env` file based on the provided template and add your private key:
The server respects `PORT` (default `5001`). Ensure it matches the proxy target in `client/vite.config.ts` if changed.

3. **Run the Node API broker**

```bash
cp .env.example .env
# edit .env and set OPENAI_API_KEY without quotes
cd server
npm run dev
```

4. Launch the application:
4. **Run the Vite client**

In a second terminal:

```bash
python app.py
cd client
npm run dev
```

5. Open your browser at [http://localhost:5000](http://localhost:5000).
5. Navigate to [http://localhost:5173](http://localhost:5173) to experience the Insight Nexus CRM suite.

## Usage workflow

- Start in **Command Center** to review real-time telemetry and AI advisories.
- Head to **Data Management** to ingest supporting docs and monitor Atlas automations.
- Launch predictions in **Predictive Strategy**; attach up to four documents to enrich outcomes.
- Use **Risk Command** to understand threat vectors and mitigation plays.
- Chat with **AI Advisor** for curated actions, then compare intelligence files inside **Collaboration Hub** for deep delta insights.

## Testing & linting

- Front-end: `cd client && npm run lint`
- Server: `cd server && npm run lint`

## Usage tips
Both projects are TypeScript/ESM aware. Adjust scripts or integrate additional tooling as needed.

- Populate the predictor form with the most recent financial metrics to receive
tailored forecasts, mitigation plans, and SWOT analysis.
- Use the advisor chat page to iterate on board decks, investor updates, or go-to-market plans.
- The application never stores your API key; it reads the value from the
environment at runtime.
## Security

## Development
- Never expose your OpenAI key to the browser. Keep it in `server/.env` or your preferred secret manager.
- Do not commit `.env` files. Keys listed here are placeholders only.
- Consider adding request authentication before deploying beyond localhost.

- Additional Python dependencies can be installed with `pip install -r requirements.txt`.
- The Flask app entry point is `app.py`; the blueprint routes live in `app/routes.py`.
- Static assets are managed in `static/` and HTML templates in `templates/`.
## Troubleshooting

## Security note
- If you encounter `Unable to generate insights` or `Unable to compare documents`, confirm the server console is running and that
`OPENAI_API_KEY` is valid.
- Update the `model` in `server/src/index.js` to match the tier available to your account if `gpt-4.1-mini` is unavailable.

Never commit your real API key to version control. Keep `.env` files private and
rotate keys regularly according to your organization’s security policies.
Enjoy orchestrating a next-generation CRM experience powered by Atlas.
21 changes: 21 additions & 0 deletions client/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'eslint:recommended',
'plugin:react-hooks/recommended',
'plugin:react-refresh/recommended',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: { jsx: true },
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['@typescript-eslint', 'react-refresh'],
rules: {
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
},
};
13 changes: 13 additions & 0 deletions client/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Insight Nexus CRM</title>
</head>
<body class="bg-canvas text-white">
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
44 changes: 44 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "insight-crm-client",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"lint": "eslint \"src/**/*.{ts,tsx}\""
},
"dependencies": {
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-scroll-area": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.1",
"axios": "^1.7.7",
"framer-motion": "^11.3.16",
"lucide-react": "^0.446.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.52.0",
"react-router-dom": "^6.26.2",
"recharts": "^2.10.4",
"zod": "^3.23.8",
"@hookform/resolvers": "^3.9.0",
"react-dropzone": "^14.2.3"
},
"devDependencies": {
"@types/node": "^22.7.4",
"@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^8.9.0",
"@typescript-eslint/parser": "^8.9.0",
"autoprefixer": "^10.4.20",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.11",
"postcss": "^8.4.47",
"tailwindcss": "^3.4.13",
"typescript": "^5.6.2",
"vite": "^5.4.10"
}
}
6 changes: 6 additions & 0 deletions client/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
80 changes: 80 additions & 0 deletions client/src/components/ChatPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { FormEvent, useMemo, useState } from 'react';
import { motion } from 'framer-motion';
import { Bot, Send } from 'lucide-react';
import { useChat } from '../hooks/useChat';

export const ChatPanel = () => {
const [message, setMessage] = useState('');
const { history, sendMessage, loading } = useChat();

const reversedHistory = useMemo(() => [...history].reverse(), [history]);

const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (!message.trim()) return;
await sendMessage(message.trim());
setMessage('');
};

return (
<div className="flex h-[30rem] flex-col rounded-3xl border border-white/5 bg-black/40">
<div className="flex items-center gap-3 border-b border-white/5 px-6 py-4">
<span className="flex h-10 w-10 items-center justify-center rounded-full bg-accent/20 text-accent">
<Bot className="h-5 w-5" />
</span>
<div>
<p className="text-sm font-semibold text-white">Atlas Copilot</p>
<p className="text-xs text-slate-400">Strategic CRM advisor</p>
</div>
</div>
<div className="flex-1 space-y-3 overflow-y-auto p-6">
{reversedHistory.map((entry, index) => (
<motion.div
key={`${entry.role}-${index}`}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className={`flex ${entry.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
<div
className={`max-w-[70%] rounded-2xl border px-4 py-3 text-sm leading-relaxed shadow-lg ${
entry.role === 'user'
? 'border-accent/40 bg-accent/20 text-white'
: 'border-white/10 bg-white/5 text-slate-100'
}`}
>
{entry.content}
</div>
</motion.div>
))}
{loading && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="flex justify-start"
>
<div className="rounded-2xl border border-white/10 bg-white/5 px-4 py-2 text-xs text-slate-300">
Atlas is synthesizing...
</div>
</motion.div>
)}
</div>
<form onSubmit={handleSubmit} className="border-t border-white/5 p-4">
<div className="flex items-center gap-3 rounded-full border border-white/10 bg-white/5 px-4 py-2">
<input
className="flex-1 bg-transparent text-sm text-white placeholder:text-slate-500 focus:outline-none"
placeholder="Request playbooks, mitigate risk, predict outcomes..."
value={message}
onChange={(event) => setMessage(event.target.value)}
/>
<button
type="submit"
disabled={loading}
className="flex h-10 w-10 items-center justify-center rounded-full bg-accent text-white transition hover:bg-accent/80 disabled:opacity-50"
>
<Send className="h-4 w-4" />
</button>
</div>
</form>
</div>
);
};
27 changes: 27 additions & 0 deletions client/src/components/GradientCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { PropsWithChildren, ReactNode } from 'react';

interface GradientCardProps extends PropsWithChildren {
title: string;
subtitle?: string;
icon?: ReactNode;
emphasis?: 'accent' | 'neon';
}

export const GradientCard = ({ title, subtitle, icon, emphasis = 'accent', children }: GradientCardProps) => (
<div
className={`gradient-border relative overflow-hidden rounded-3xl border border-white/5 bg-slate-950/60 p-7 shadow-xl shadow-black/20 backdrop-blur-2xl transition ${
emphasis === 'accent' ? 'hover:border-accent/60' : 'hover:border-neon/60'
}`}
>
<div className="relative z-10 space-y-4">
<div className="flex items-start justify-between gap-4">
<div>
<p className="text-xs uppercase tracking-[0.35em] text-slate-400">{subtitle}</p>
<h3 className="text-2xl font-semibold text-white">{title}</h3>
</div>
{icon && <span className="text-accent">{icon}</span>}
</div>
<div className="space-y-4 text-sm text-slate-200">{children}</div>
</div>
</div>
);
18 changes: 18 additions & 0 deletions client/src/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { PropsWithChildren } from 'react';
import { Navigation } from './Navigation';

export const Layout = ({ children }: PropsWithChildren) => (
<div className="min-h-screen grid grid-cols-[280px_1fr] bg-canvas text-slate-100">
<aside className="relative border-r border-white/5 bg-slate-950/70 backdrop-blur-xl">
<div className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-accent/20 via-neon/20 to-transparent" />
<Navigation />
</aside>
<main className="relative overflow-y-auto">
<div className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-accent/20 to-neon/20" />
<div className="relative px-10 pb-12 pt-16">
<div className="absolute inset-0 bg-[radial-gradient(circle_at_top_right,rgba(75,107,251,0.12),transparent_55%)]" />
<div className="relative z-10 space-y-12">{children}</div>
</div>
</main>
</div>
);
Loading