diff --git a/FUTURE_FEATURES.md b/FUTURE_FEATURES.md new file mode 100644 index 0000000..99eac61 --- /dev/null +++ b/FUTURE_FEATURES.md @@ -0,0 +1,83 @@ +# Removed / Not-Yet-Built Features + +These sidebar pages were removed from `src/pages/dashboard/DashboardLayout.tsx`'s +nav because no backend feature exists for them — they were showing fully +hardcoded mock data with no real API behind them. Removed rather than shipped +with fake data, per the "no mock data" rule. + +## 1. Billing / Subscriptions + +**Was at:** `/dashboard/billing` (deleted: `src/pages/dashboard/Billing.tsx`) + +**What it showed (all fake):** hardcoded "Pro Plan $49/month", 3-tier plan +comparison cards, fake invoice history table. + +**What's needed to rebuild for real:** +- Backend: `plan`, `subscription_status`, `stripe_customer_id` columns on + `users` table (mentioned as a possibility in `HANDOFF.md` from the original + handoff, never implemented) +- Real Stripe integration — the old Next.js frontend had + `src/app/api/stripe/{checkout,webhook}/route.ts` but they were dead code + (never called, placeholder keys, no backend wiring). Since Vite can't host + secret-key server logic, this needs to live as new endpoints on the FastAPI + backend (`Multi-Tenant-ODOO-MCP`), not the frontend. +- New endpoints: `POST /api/billing/checkout`, `POST /api/billing/webhook`, + `GET /api/billing/invoices` +- Frontend: rebuild the page against those real endpoints + +## 2. Team / Multi-User Accounts + +**Was at:** `/dashboard/team` (route never existed — dead nav link, 404) + +**What's needed:** an entirely new multi-user-per-account concept. Currently +one `users` row = one account = one set of Odoo credentials/API keys. Team +support means either: +- A new `team_members` table linking multiple `users` rows to one billing + account with role-based permissions, or +- An `account_id` grouping concept above `users` + +This is a significant schema/auth redesign, not a small addition. + +## 3. Tools & Permissions + +**Was at:** `/dashboard/tools` (route never existed — dead nav link, 404) + +**What's needed:** per-user, per-tool enable/disable toggles for the 47 MCP +tools currently registered in `server.py`'s `TOOLS` list. Would need: +- A new `disabled_tools` table or JSON column on `users` +- A check in `call_tool()` (`server.py`) before dispatch: reject if the tool + is disabled for that user +- Frontend: a page listing all 47 tools grouped by module (connection, + discovery, records, workflow, smart) with toggles, calling a new + `PATCH /api/tools/{name}` endpoint + +## 4. AI Setup Guides + +**Was at:** `/dashboard/guides` (route never existed — dead nav link, 404) + +**What's needed:** this is content, not really a backend feature — step-by-step +setup instructions for Claude, Codex, Cursor, Windsurf. Partially exists +already: `/dashboard/endpoints` already has a working Claude Desktop JSON +config generator with the real API key and MCP URL. This page could be built +as a pure frontend content page (no backend needed) with setup docs for the +other clients, following the same pattern already in `Endpoints.tsx`. + +## 5. Settings (account settings / change password while logged in) + +**Was at:** `/dashboard/settings` (route never existed — dead nav link, 404) + +**What's needed:** currently the only way to change a password is the +forgot-password token-based flow (`/forgot-password` → `/reset-password`). +There's no "logged in, change my password" endpoint. Would need: +- Backend: `POST /api/account/change-password` (current password + new + password, no token needed since the user is already authenticated) +- Frontend: a settings page with that form, plus maybe email/display-name + fields if those become editable later + +## 6. Support link + +**Was at:** footer link `/dashboard/support` (route never existed — dead +link, removed entirely from `DashboardLayout.tsx`, not just hidden) + +Trivial — just needs a real destination (mailto link, external help site, or +a real support-ticket feature) whenever there's something to point it at. diff --git a/src/App.tsx b/src/App.tsx index 75808d9..bc22f54 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,7 +8,6 @@ import ResetPassword from './pages/ResetPassword'; import DashboardLayout from './pages/dashboard/DashboardLayout'; import DashboardHome from './pages/dashboard/DashboardHome'; import Connections from './pages/dashboard/Connections'; -import Billing from './pages/dashboard/Billing'; import Endpoints from './pages/dashboard/Endpoints'; import Tokens from './pages/dashboard/Tokens'; import Usage from './pages/dashboard/Usage'; @@ -33,7 +32,6 @@ export default function App() { }> } /> } /> - } /> } /> } /> } /> diff --git a/src/pages/dashboard/Billing.tsx b/src/pages/dashboard/Billing.tsx deleted file mode 100644 index 96bbeca..0000000 --- a/src/pages/dashboard/Billing.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { Check } from 'lucide-react'; - -const PLANS = [ - { name: 'Starter', price: 19, current: false, features: ['1 connection', '1 MCP endpoint', 'Read-only', '1,000 req/mo'] }, - { name: 'Pro', price: 49, current: true, features: ['3 connections', 'Read/Write', 'Access logs', '10,000 req/mo'] }, - { name: 'Agency', price: 149, current: false, features: ['25 workspaces', 'White-label', 'Team members', '50,000 req/mo'] }, -]; - -export default function Billing() { - return ( -
-
-

Billing

-

Manage your subscription and payment details.

-
- - {/* Current plan */} -
-
-
-

Current Plan

-

Pro Plan

-

$49/month · Renews July 29, 2026

-
- Active -
-
- - -
-
- - {/* Plan comparison */} -
-

Change Plan

-
- {PLANS.map((plan) => ( -
-

{plan.name}

-

${plan.price}/mo

-
    - {plan.features.map((f) => ( -
  • - {f} -
  • - ))} -
- {plan.current ? ( -
Current Plan
- ) : ( - - )} -
- ))} -
-
- - {/* Invoice history */} -
-

Invoice History

- - - - - - - - - - - {[ - { date: 'Jun 29, 2026', desc: 'Pro Plan — Monthly', amount: '$49.00', status: 'Paid' }, - { date: 'May 29, 2026', desc: 'Pro Plan — Monthly', amount: '$49.00', status: 'Paid' }, - { date: 'Apr 29, 2026', desc: 'Pro Plan — Monthly', amount: '$49.00', status: 'Paid' }, - ].map((row, i) => ( - - - - - - - ))} - -
DateDescriptionAmountStatus
{row.date}{row.desc}{row.amount} - {row.status} -
-
-
- ); -} diff --git a/src/pages/dashboard/DashboardHome.tsx b/src/pages/dashboard/DashboardHome.tsx index fea4129..40139b9 100644 --- a/src/pages/dashboard/DashboardHome.tsx +++ b/src/pages/dashboard/DashboardHome.tsx @@ -1,30 +1,75 @@ -import { useEffect, useState } from 'react'; -import { api, session, type MeResult } from '@/lib/api'; -import { Cpu, Globe, BarChart2, Copy, RotateCcw, ExternalLink, Activity } from 'lucide-react'; +import { useEffect, useState, useCallback } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + api, session, type MeResult, type AnalyticsSummary, type TimeseriesBucket, type LogEntry, +} from '@/lib/api'; +import { + Cpu, Globe, Copy, RotateCcw, ExternalLink, Activity, CheckCircle2, XCircle, Key, +} from 'lucide-react'; +import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; import { clsx } from 'clsx'; const MCP_URL = `${import.meta.env.VITE_API_URL ?? 'https://odoo-mcp.thedomainnest.com'}/mcp/sse`; -const RECENT_ACTIVITY = [ - { time: '2 mins ago', event: 'Connection sync', status: 'Success', detail: 'Odoo connection is healthy' }, - { time: '5 mins ago', event: 'MCP request', status: 'Success', detail: 'search_invoices' }, - { time: '12 mins ago', event: 'Token rotated', status: 'Success', detail: 'New token generated' }, - { time: '1 hour ago', event: 'Connection sync', status: 'Success', detail: 'Odoo connection is healthy' }, -]; +function isoDaysAgo(days: number): string { + const d = new Date(); + d.setUTCDate(d.getUTCDate() - days); + return d.toISOString(); +} export default function DashboardHome() { + const navigate = useNavigate(); const [me, setMe] = useState(null); + const [summary, setSummary] = useState(null); + const [timeseries, setTimeseries] = useState([]); + const [recentLogs, setRecentLogs] = useState([]); + const [activeKeyCount, setActiveKeyCount] = useState(null); const [loading, setLoading] = useState(true); + const [rotating, setRotating] = useState(false); - useEffect(() => { + const load = useCallback(() => { const key = session.getKey(); if (!key) return; - api.me(key).then(setMe).catch(console.error).finally(() => setLoading(false)); + setLoading(true); + const since = isoDaysAgo(30); + Promise.all([ + api.me(key), + api.analyticsSummary(key, since), + api.analyticsTimeseries(key, since, undefined, 'day'), + api.listLogs(key, 1, 5), + api.listApiKeys(key), + ]) + .then(([meResult, summaryResult, timeseriesResult, logsResult, keysResult]) => { + setMe(meResult); + setSummary(summaryResult); + setTimeseries(timeseriesResult.buckets); + setRecentLogs(logsResult.items); + setActiveKeyCount(keysResult.keys.filter((k) => k.is_active).length); + }) + .catch(console.error) + .finally(() => setLoading(false)); }, []); + useEffect(load, [load]); + const instance = me?.odoo_instances?.[0]; - const requestsUsed = 2431; - const requestsLimit = 10000; + + async function rotateLegacyKey() { + const key = session.getKey(); + if (!key) return; + if (!confirm('This regenerates your original signup API key. The old key stops working immediately — any client still using it will lose access. Continue?')) return; + setRotating(true); + try { + const result = await api.regenKey(key); + session.save(result.api_key, me?.email ?? ''); + alert('Key rotated. Update any MCP clients using the old key with the new one shown on the API Tokens page.'); + load(); + } catch (err) { + alert(err instanceof Error ? err.message : 'Failed to rotate key'); + } finally { + setRotating(false); + } + } return (
@@ -33,142 +78,156 @@ export default function DashboardHome() {

Welcome back{me ? `, ${me.email}` : ''}.

- {/* Stat cards */} + {/* Stat cards — all real, from /api/analytics/summary and /api/me */}
💎} - color="bg-purple-50" + label="Requests (30 days)" + value={loading ? '—' : (summary?.total_calls ?? 0).toLocaleString()} + icon={} + color="bg-blue-50" /> Active} - sub="All systems operational" - icon={} - color="bg-blue-50" + label="Successful" + value={loading ? '—' : (summary?.success_count ?? 0).toLocaleString()} + icon={} + color="bg-green-50" + /> + } + color="bg-red-50" /> } - color="bg-green-50" - /> - -
-
-
- {Math.round((requestsUsed / requestsLimit) * 100)}% used -
- } - icon={} - color="bg-orange-50" + icon={} + color="bg-purple-50" />
- {/* Odoo Connection Health */} + {/* Odoo Connection Health — real data from /api/me */}

Odoo Connection Health

{instance ? (
- - Healthy} /> -
) : (

No Odoo instance connected yet.{' '} - Add one → +

)}
- {/* Quick Actions */} + {/* Quick Actions — every action is real, no no-ops */}

Quick Actions

- {[ - { label: 'Copy MCP URL', icon: Copy, action: () => navigator.clipboard.writeText(MCP_URL) }, - { label: 'Rotate Token', icon: RotateCcw, action: () => {} }, - { label: 'Open Claude Setup', icon: ExternalLink, action: () => {} }, - { label: 'Open Codex Setup', icon: ExternalLink, action: () => {} }, - { label: 'View Logs', icon: Activity, action: () => {} }, - ].map(({ label, icon: Icon, action }) => ( - - ))} + + + + +
- {/* Usage Overview */} + {/* Usage Overview — real daily call counts from /api/analytics/timeseries */}
-

Usage Overview

-
- {[40, 65, 45, 80, 55, 90, 70, 85, 60, 95, 75, 100, 60, 85].map((h, i) => ( -
- ))} -
-
- Jun 1Jun 15Jun 29 -
-

{requestsUsed.toLocaleString()}

-

Requests this month

+

Usage Overview (30 days)

+ {timeseries.length === 0 && !loading ? ( +

No activity yet.

+ ) : ( + + + + + + + + + + )} +

{(summary?.total_calls ?? 0).toLocaleString()}

+

+ Requests in the last 30 days · {activeKeyCount ?? '—'} active API key{activeKeyCount === 1 ? '' : 's'} +

- {/* Recent Activity */} + {/* Recent Activity — real data from /api/logs */}

Recent Activity

- View all logs → +
- - - - - - - - - - - {RECENT_ACTIVITY.map((row, i) => ( - - - - - + {recentLogs.length === 0 && !loading ? ( +

No tool calls yet. Connect an MCP client to get started.

+ ) : ( +
TimeEventStatusDetails
{row.time}{row.event} - - {row.status} - - {row.detail}
+ + + + + + - ))} - -
TimeToolStatusDuration
+ + + {recentLogs.map((log) => ( + + {log.started_at} + {log.tool_name} + + + + {log.success ? 'Success' : 'Error'} + + + {log.duration_ms} ms + + ))} + + + )}
); } -function StatCard({ label, value, sub, icon, color }: { - label: string; value: React.ReactNode; sub: React.ReactNode; icon: React.ReactNode; color: string; +function StatCard({ label, value, icon, color }: { + label: string; value: React.ReactNode; icon: React.ReactNode; color: string; }) { return (
@@ -177,7 +236,6 @@ function StatCard({ label, value, sub, icon, color }: {
{icon}

{value}

-
{sub}
); } diff --git a/src/pages/dashboard/DashboardLayout.tsx b/src/pages/dashboard/DashboardLayout.tsx index 0e5d8d1..3e889d1 100644 --- a/src/pages/dashboard/DashboardLayout.tsx +++ b/src/pages/dashboard/DashboardLayout.tsx @@ -2,23 +2,22 @@ import { useEffect, useState } from 'react'; import { useNavigate, useLocation, Link, Outlet } from 'react-router-dom'; import { session } from '@/lib/api'; import { - LayoutDashboard, Globe, Cpu, BookOpen, Shield, BarChart2, - FileText, CreditCard, Key, Users, Settings, HelpCircle, LogOut, Menu, X, + LayoutDashboard, Globe, Cpu, BarChart2, FileText, Key, LogOut, Menu, } from 'lucide-react'; import { clsx } from 'clsx'; +// NOTE: AI Setup Guides, Tools & Permissions, Billing, Team, and Settings were +// removed from this nav because no backend feature exists for them yet +// (no team/multi-user system, no per-tool permission toggles, no real +// subscription/billing system). See FUTURE_FEATURES.md for what to rebuild +// and how, once those backend features are designed. const NAV = [ { label: 'Dashboard', href: '/dashboard', icon: LayoutDashboard }, { label: 'Odoo Connections', href: '/dashboard/connections', icon: Globe }, { label: 'MCP Endpoints', href: '/dashboard/endpoints', icon: Cpu }, - { label: 'AI Setup Guides', href: '/dashboard/guides', icon: BookOpen }, - { label: 'Tools & Permissions', href: '/dashboard/tools', icon: Shield }, { label: 'Usage', href: '/dashboard/usage', icon: BarChart2 }, { label: 'Logs', href: '/dashboard/logs', icon: FileText }, - { label: 'Billing', href: '/dashboard/billing', icon: CreditCard }, { label: 'API Tokens', href: '/dashboard/tokens', icon: Key }, - { label: 'Team', href: '/dashboard/team', icon: Users }, - { label: 'Settings', href: '/dashboard/settings', icon: Settings }, ]; export default function DashboardLayout() { @@ -77,10 +76,6 @@ export default function DashboardLayout() { {/* Bottom */}
- - Support -