pgx
PGX — Lightweight Postgres for AI agents. Written in Rust. Thousands of isolated databases in one runtime. 🐘
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗Milton is a web application that helps small and medium businesses understand their performance through automated data analysis and KPI dashboards. Users upload their existing business data files (Excel/CSV), and the app uses AI to classify and map that data, calculate relevant KPIs, and generate insights — without requiring any technical knowledge.
Target users: Startup founders, SMB owners/operators (no technical background required)
Core value: Upload your business data (bookings, orders, transactions, memberships) → get automatic KPI dashboards and AI-generated insights.
Supported business types:
Key capabilities:
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router, TypeScript) |
| UI | React 19, Tailwind CSS 4, Radix UI, Shadcn/ui, Lucide icons |
| Database & Auth | Supabase (PostgreSQL + Row Level Security) |
| AI | Vercel AI SDK + OpenAI API (gpt-4o-mini) |
| Charts | Recharts |
| Data Model Visualizer | React Flow |
| File Parsing | xlsx (Excel), PapaParse (CSV) |
| PDF Generation | jsPDF + html2canvas |
| Form Handling | React Hook Form + Zod |
| Testing | Vitest (unit), Playwright (E2E) |
git clone <repo-url>
cd milton
npm install
Create a .env.local file:
# OpenAI
OPENAI_API_KEY=your-openai-api-key
# Supabase (public — safe in browser)
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
# Supabase (secret — server-side only, bypasses RLS)
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
Where to find Supabase keys: Dashboard → Settings → API
npm run dev # development server at http://localhost:3000
npm run build # production build
npm run test # run unit tests
┌─────────────────────────────────────────────────────────────────┐
│ Next.js App Router │
│ │
│ Pages (app/) API Routes (app/api/) │
│ ├── /auth ├── /auth/signup-complete │
│ ├── /onboarding ├── /ai/* (classify, analyze, insights) │
│ ├── /dashboard ├── /data/* (parse, upload, status) │
│ ├── /management ├── /kpis/* (calculate, select) │
│ └── / └── /analytics/* (per business type) │
└───────────────────────────────┬─────────────────────────────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
Supabase OpenAI API Client Browser
(Auth + DB) (gpt-4o-mini) (React components)
User upload (CSV/Excel)
│
▼
/api/data/parse — Extract sheets, headers, sample rows
│
▼
/api/ai/dataset-classifier — Identify which business table this file maps to
│
▼
Data Mapping UI — User confirms/adjusts column → field mappings
│
▼
Value Normalization — Case-correct categories, normalize dates
│
▼
/api/data/upload-model-table — Insert rows into model_data table (JSONB)
│
▼
/api/kpis/calculate — Query model_data, aggregate, return KPI values
│
▼
Dashboard — Display KPI cards, charts, AI insights
auth.users, app creates profiles and companies records/onboarding/chat) — Describe your business in natural language; AI identifies business type (fitness_studio, restaurant, ecommerce, etc.)/onboarding/data-sources) — Select which data sources you have (bookings, transactions, orders, etc.)/onboarding/upload) — Upload Excel or CSV files; AI classifies each sheet to a canonical table/onboarding/kpi-selection) — Choose and rank which KPIs to track/dashboard with KPIs populatedAccessible at /management/ (requires admin role):
milton/
├── app/ # Next.js App Router
│ ├── (root)/page.tsx # Landing page
│ ├── auth/ # Login, signup, OAuth callback
│ ├── onboarding/ # 6-step onboarding flow
│ │ ├── chat/ # AI business model chat
│ │ ├── data-sources/ # Source selection
│ │ ├── upload/ # File upload step
│ │ ├── model/ # Data model viewer
│ │ └── kpi-selection/ # KPI selection step
│ ├── dashboard/ # Main app
│ │ ├── page.tsx # KPI dashboard home
│ │ ├── analytics/ # Analytics & drill-downs
│ │ ├── data/ # Data management
│ │ ├── upload/ # Additional uploads
│ │ ├── reporting/ # PDF report generation
│ │ ├── account/ # User account
│ │ └── settings/ # Dashboard settings
│ ├── management/ # Admin panel
│ │ ├── dashboard/ # Admin overview
│ │ ├── kpis/ # KPI CRUD
│ │ ├── templates/ # Template CRUD
│ │ └── data-tables/ # Data table CRUD
│ └── api/ # API routes (see API Reference)
│
├── components/
│ ├── dashboard/ # Dashboard & upload UI components
│ ├── onboarding/ # Onboarding wizard components
│ ├── management/ # Admin panel components
│ ├── layout/ # Navigation, providers, theme
│ ├── auth/ # Login/signup forms
│ ├── ai-elements/ # Chat UI components
│ └── ui/ # Shadcn/Radix primitives
│
├── lib/
│ ├── model/
│ │ ├── transform.ts # Core model types (TableDef, ModelProposal, etc.) + graph logic
│ │ └── default-models.ts # Hardcoded fallback models (fitness, restaurant, ecom)
│ ├── kpi-calculations/ # One file per KPI calculator function
│ │ ├── fitness/ # Fitness studio KPIs
│ │ ├── restaurant/ # Restaurant KPIs
│ │ ├── ecom/ # E-commerce KPIs
│ │ ├── financial/ # Cross-sector financial KPIs
│ │ ├── fieldAccessors.ts # Flexible field reading from JSONB rows
│ │ └── index.ts # Exports all calculators
│ ├── ai/ # AI helper modules
│ │ ├── dashboard-context.ts # Build AI context from KPI data
│ │ ├── normalize-values.ts # AI value normalization
│ │ ├── generate-ids.ts # AI-generated IDs for table rows
│ │ ├── insights.ts # Generic insight framework
│ │ ├── fitness-studio-insights.ts
│ │ └── restaurant-insights.ts
│ ├── types/
│ │ ├── data.ts # Shared data types (DataTable, TransactionData, etc.)
│ │ ├── kpi.ts # KPI types and selectors
│ │ ├── pipeline.ts # CRM/pipeline types
│ │ └── report.ts # Report data structures
│ ├── supabase/
│ │ ├── client.ts # Browser Supabase client
│ │ └── server.ts # Server-side Supabase client (service role)
│ ├── data-table-service.ts # Data table CRUD (Supabase)
│ ├── business-models.ts # BusinessModel record CRUD
│ ├── dashboard-kpis.ts # KPI routing by business type
│ ├── kpi-recipe-service.ts # Map KPI IDs → calculator functions
│ ├── kpi-availability.ts # Which KPIs are available
│ ├── report-data-service.ts # Gather data for PDF reports
│ └── utils.ts # cn(), normalizeDateValue(), etc.
│
├── types/
│ └── schema.ts # ColumnMapping and other shared types
│
├── supabase/ # DB migrations
├── test-data/ # Sample data files for testing
└── tests/ # Test suites
├── unit/ # Vitest unit tests
└── smoke/ # Playwright E2E tests
| Table | Purpose |
|---|---|
auth.users | Supabase-managed authentication |
profiles | User profile (id, user_id, company_id, full_name, email); see milton_admins for admin flag |
companies | Tenant record; linked to creator user |
business_models | Per-company business type, KPI selections, canonical data model, onboarding answers |
data_tables | Admin-defined canonical table schemas (fields as JSONB array) |
model_data | All uploaded business data — rows stored as JSONB, scoped to company_id + model_table_id |
kpis | KPI definitions (name, formula reference, business_types, is_published) |
business_model_templates | Templates per business type (required tables, relationships, KPI lists) |
custom_datasets | Metadata about uploaded files (schema, source info) |
dashboard_insights | Cached AI-generated insight text |
auth.users
└── profiles (one-to-one)
└── companies (one-to-many via created_by)
└── business_models (one-to-one)
└── model_data (one-to-many) ← all uploaded rows live here
└── data_tables (many-to-one via model_table_id)
This is the central storage table for all uploaded business data:
model_data (
id uuid PRIMARY KEY,
company_id uuid REFERENCES companies(id),
model_table_id uuid REFERENCES data_tables(id),
data jsonb, -- the actual row data
indexed_fields jsonb, -- extracted fields for faster querying
created_at timestamptz
)
All KPI calculations query this table, filtering by company_id and model_table_id, then reading the flexible data JSONB column.
company_id matches their companyThe "business model" represents a company's canonical data structure — which tables they use, what relationships exist, and what KPIs are relevant.
lib/model/default-models.ts) — fallback definitions used during onboarding when no Supabase template is availablebusiness_models.canonical_model as JSONB) — the actual model applied to a company, derived from the template + any user customizationsThe core types in lib/model/transform.ts:
interface FieldDef {
name: string;
type: "string" | "number" | "integer" | "date" | "currency" | "boolean";
required: boolean;
primaryKey?: boolean;
references?: { table: string; field?: string } | null;
allowedValues?: string[]; // For enum/categorical fields
displayName?: string; // Human-readable label (e.g., "Order Items" vs "ecom_order_items")
}
interface TableDef {
name: string; // Internal name used in DB (e.g., "ecom_orders")
displayName?: string; // User-facing label (e.g., "Orders")
fields: FieldDef[];
fileMapping?: { fileName?: string; columnMap?: Record<string, string> };
}
interface ModelProposal {
businessType: string;
recommendedTables: TableDef[];
relationships: RelationshipDef[];
}
| Key | Display Name | Tables |
|---|---|---|
fitness_studio | Fitness Studio | Customers, Classes, Instructors, Bookings, Payments |
restaurant | Restaurant | Tables, Menu Items, Orders, Order Items, Reservations |
ecommerce | E-commerce | Orders, Order Items, Products, Customers, Marketing Spend |
KPIs are stored in the kpis Supabase table. Each KPI has:
name — display namedefinition — descriptionformula — optional formula string (documentation)required_data — array of data_tables.id UUIDs the KPI needsbusiness_types — which business types this KPI applies tois_published — whether visible to users (drafts hidden)Each KPI has a dedicated TypeScript function in lib/kpi-calculations/:
// Common signature
async function calculateXxx(
supabase: SupabaseClient,
userId: string,
fromDate: string,
toDate: string
): Promise<{
currentValue: number;
historicalData: Array<{ period: string; value: number }>;
}>;
Fitness Studio KPIs:
Financial KPIs (cross-sector):
Restaurant KPIs:
E-commerce KPIs:
When a user opens the KPI selector, the app:
GET /api/data/status → gets which data_tables have uploaded data (by UUID)required_data UUIDs have dataSelected KPIs appear on the dashboard as:
The app uses two OpenAI integration patterns:
Used for conversational interfaces:
/app/onboarding/chat)Used for one-shot classification/analysis tasks:
/api/ai/dataset-classifier) — which business table does a file map to?/api/ai/business-model-analyzer) — what type of business is this?/api/data/normalize-values) — correct inconsistent categorical valueslib/ai/generate-ids.ts) — human-readable IDs for uploaded rowslib/ai/)All AI calls use gpt-4o-mini for cost efficiency. The model is not configurable per-call; it is set globally in each AI helper.
/auth/signupauth.usersPOST /api/auth/signup-complete creates profiles (basic info only) and companies records| Role | Access |
|---|---|
user | Own company data only; dashboard, uploads, KPI selection |
admin | All management routes; KPI/template/table CRUD |
Admin role is stored in public.milton_admins. Checked server-side via isUserAdminServer() in lib/profile-service-server.ts, which calls the is_milton_admin() RPC (migration 020).
lib/supabase/client.ts — browser client (anon key, subject to RLS)lib/supabase/server.ts — server client (service role key, bypasses RLS)| Method | Endpoint | Description |
|---|---|---|
| POST | /api/data/parse | Parse Excel/CSV; returns sheets, headers, sample rows, all rows |
| POST | /api/data/upload-model-table | Insert mapped rows into model_data table |
| DELETE | /api/data/delete-model-table | Remove data for a specific table |
| GET | /api/data/status | Which data tables have uploaded data (with row counts) |
| GET | /api/data/table-names | Resolve table UUIDs to names |
| GET | /api/data/table-preview | Preview first N rows of a table |
| POST | /api/data/normalize-values | AI-normalize categorical field values |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/kpis/calculate | Calculate all selected KPIs for a date range |
| POST | /api/kpis/series | Get historical time series for a KPI |
| GET | /api/kpis/selected | Get user's selected KPI IDs and display modes |
| POST | /api/kpis/select | Save KPI selection |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/ai/business-model-analyzer | Classify business type from description |
| POST | /api/ai/dataset-classifier | Classify uploaded dataset to a canonical table |
| POST | /api/ai/onboarding | Stream onboarding chat responses |
| POST | /api/ai/insight-generator | Generate AI insights from KPI data |
| POST | /api/milton/ask | Dashboard chat assistant |
| Endpoint | Business Type | Returns |
|---|---|---|
GET /api/analytics/fitness-studio/kpis | Fitness | All fitness KPI values |
GET /api/analytics/ecommerce/kpis | E-commerce | All ecom KPI values |
GET /api/analytics/restaurant/overview | Restaurant | Revenue, covers, etc. |
Various /api/analytics/fitness-studio/* | Fitness | Members, instructors, classes, cash flow, charts |
| Method | Endpoint | Description |
|---|---|---|
| GET/POST | /api/admin/kpis | List / create KPIs |
| PUT/DELETE | /api/admin/kpis/[id] | Update / delete KPI |
| GET | /api/admin/kpis/impact/[id] | Impact analysis before delete |
| GET/POST | /api/admin/templates | List / create templates |
| GET/PUT/DELETE | /api/admin/templates/[key] | Manage specific template |
| GET/POST | /api/admin/data-tables | List / create data table definitions |
| PUT/DELETE | /api/admin/data-tables/[id] | Update / delete data table |
| GET | /api/admin/data-tables/impact/[id] | Impact analysis before delete |
npm run test:unit # run once
npm run test:unit:watch # watch mode
Located in tests/unit/. Cover:
npx playwright test # run all E2E tests
Located in tests/smoke/. Cover:
Sample data files for manual testing are in test-data/ organized by business type (e.g., test-data/restaurant/).
| File | Purpose |
|---|---|
lib/model/transform.ts | Core data model types and transformation logic |
lib/model/default-models.ts | Hardcoded fallback business models |
lib/kpi-calculations/fieldAccessors.ts | Flexible field reading from JSONB rows |
lib/kpi-calculations/index.ts | Exports all KPI calculator functions |
lib/utils.ts | normalizeDateValue(), cn(), shared utilities |
lib/data-table-service.ts | Read data table definitions from Supabase |
lib/dashboard-kpis.ts | Routes KPI calculation requests by business type |
lib/kpi-recipe-service.ts | Maps KPI IDs to calculator functions |
app/api/data/upload-model-table/route.ts | Central upload handler with ID generation, FK resolution, normalization |
app/api/data/parse/route.ts | File parsing endpoint (Excel/CSV → rows) |
components/dashboard/ModelTableDetailView.tsx | Single-table upload + mapping UI |
components/dashboard/MultiFileUploadView.tsx | Multi-file drag-drop upload flow |
components/dashboard/data-mapping-confirmation.tsx | Column mapping modal |
components/dashboard/kpi-selector.tsx | KPI selection dialog |