Add Supabase integration for trading journal

This commit is contained in:
root
2026-02-23 19:27:47 +00:00
parent a0a2fc1d40
commit 2bb58e041b
5 changed files with 273 additions and 23 deletions
+21 -21
View File
@@ -1,39 +1,39 @@
import { NextRequest, NextResponse } from 'next/server'
import * as fs from 'fs'
import * as path from 'path'
const dataFile = path.join(process.cwd(), 'trading-trades.json')
import { supabase } from '@/lib/supabase'
export async function GET() {
try {
let trades = []
if (fs.existsSync(dataFile)) {
trades = JSON.parse(fs.readFileSync(dataFile, 'utf-8'))
}
return NextResponse.json({ trades })
const { data: trades, error } = await supabase
.from('trades')
.select('*')
.order('opened_at', { ascending: false })
if (error) throw error
return NextResponse.json({ trades: trades || [] })
} catch (error) {
return NextResponse.json({ trades: [] })
console.error('Supabase error:', error)
return NextResponse.json({ trades: [], error: 'Failed to fetch' }, { status: 500 })
}
}
export async function POST(request: NextRequest) {
try {
const body = await request.json()
let trades = []
if (fs.existsSync(dataFile)) {
trades = JSON.parse(fs.readFileSync(dataFile, 'utf-8'))
}
trades.push({
const { data, error } = await supabase
.from('trades')
.insert([{
...body,
id: Date.now().toString(),
openedAt: new Date().toISOString()
})
opened_at: new Date().toISOString(),
}])
.select()
fs.writeFileSync(dataFile, JSON.stringify(trades, null, 2))
return NextResponse.json({ success: true })
if (error) throw error
return NextResponse.json({ success: true, trade: data?.[0] })
} catch (error) {
console.error('Supabase error:', error)
return NextResponse.json({ error: 'Failed to save' }, { status: 500 })
}
}
+66
View File
@@ -0,0 +1,66 @@
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL || 'https://mgqefaxhfmgkoqeachgi.supabase.co'
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || 'sb_publishable_T2pJmKz7HIFy4WuT-6eknw_kYckbItP'
export const supabase = createClient(supabaseUrl, supabaseAnonKey)
// Database types
export interface Database {
public: {
Tables: {
trades: {
Row: {
id: string
user_id: string
pair: string
direction: 'long' | 'short'
entry_price: number
exit_price?: number
status: 'open' | 'closed'
is_demo: boolean
trader_style: string
setup: string
timeframe: string
pnl?: number
pnl_percent?: number
opened_at: string
closed_at?: string
created_at: string
}
Insert: Omit<Database['public']['Tables']['trades']['Row'], 'id' | 'created_at'>
Update: Partial<Database['public']['Tables']['trades']['Insert']>
}
tasks: {
Row: {
id: string
user_id: string
title: string
description?: string
status: 'pending' | 'in_progress' | 'completed'
priority: 'low' | 'medium' | 'high'
due_date?: string
created_at: string
}
Insert: Omit<Database['public']['Tables']['tasks']['Row'], 'id' | 'created_at'>
Update: Partial<Database['public']['Tables']['tasks']['Insert']>
}
leads: {
Row: {
id: string
user_id: string
name: string
business_name?: string
phone?: string
email?: string
source?: string
status: 'new' | 'contacted' | 'qualified' | 'won' | 'lost'
notes?: string
created_at: string
}
Insert: Omit<Database['public']['Tables']['leads']['Row'], 'id' | 'created_at'>
Update: Partial<Database['public']['Tables']['leads']['Insert']>
}
}
}
}
+105
View File
@@ -11,6 +11,7 @@
"@google/genai": "^1.39.0",
"@google/generative-ai": "^0.24.1",
"@stripe/stripe-js": "^8.7.0",
"@supabase/supabase-js": "^2.97.0",
"@vapi-ai/web": "^2.5.2",
"chartjs-plugin-zoom": "^2.2.0",
"framer-motion": "^12.23.12",
@@ -968,6 +969,86 @@
"node": ">=12.16"
}
},
"node_modules/@supabase/auth-js": {
"version": "2.97.0",
"resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.97.0.tgz",
"integrity": "sha512-2Og/1lqp+AIavr8qS2X04aSl8RBY06y4LrtIAGxat06XoXYiDxKNQMQzWDAKm1EyZFZVRNH48DO5YvIZ7la5fQ==",
"license": "MIT",
"dependencies": {
"tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/functions-js": {
"version": "2.97.0",
"resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.97.0.tgz",
"integrity": "sha512-fSaA0ZeBUS9hMgpGZt5shIZvfs3Mvx2ZdajQT4kv/whubqDBAp3GU5W8iIXy21MRvKmO2NpAj8/Q6y+ZkZyF/w==",
"license": "MIT",
"dependencies": {
"tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/postgrest-js": {
"version": "2.97.0",
"resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.97.0.tgz",
"integrity": "sha512-g4Ps0eaxZZurvfv/KGoo2XPZNpyNtjth9aW8eho9LZWM0bUuBtxPZw3ZQ6ERSpEGogshR+XNgwlSPIwcuHCNww==",
"license": "MIT",
"dependencies": {
"tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/realtime-js": {
"version": "2.97.0",
"resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.97.0.tgz",
"integrity": "sha512-37Jw0NLaFP0CZd7qCan97D1zWutPrTSpgWxAw6Yok59JZoxp4IIKMrPeftJ3LZHmf+ILQOPy3i0pRDHM9FY36Q==",
"license": "MIT",
"dependencies": {
"@types/phoenix": "^1.6.6",
"@types/ws": "^8.18.1",
"tslib": "2.8.1",
"ws": "^8.18.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/storage-js": {
"version": "2.97.0",
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.97.0.tgz",
"integrity": "sha512-9f6NniSBfuMxOWKwEFb+RjJzkfMdJUwv9oHuFJKfe/5VJR8cd90qw68m6Hn0ImGtwG37TUO+QHtoOechxRJ1Yg==",
"license": "MIT",
"dependencies": {
"iceberg-js": "^0.8.1",
"tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/supabase-js": {
"version": "2.97.0",
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.97.0.tgz",
"integrity": "sha512-kTD91rZNO4LvRUHv4x3/4hNmsEd2ofkYhuba2VMUPRVef1RCmnHtm7rIws38Fg0yQnOSZOplQzafn0GSiy6GVg==",
"license": "MIT",
"dependencies": {
"@supabase/auth-js": "2.97.0",
"@supabase/functions-js": "2.97.0",
"@supabase/postgrest-js": "2.97.0",
"@supabase/realtime-js": "2.97.0",
"@supabase/storage-js": "2.97.0"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@swc/helpers": {
"version": "0.5.15",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
@@ -992,6 +1073,12 @@
"undici-types": "~7.16.0"
}
},
"node_modules/@types/phoenix": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz",
"integrity": "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==",
"license": "MIT"
},
"node_modules/@types/react": {
"version": "19.2.10",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz",
@@ -1012,6 +1099,15 @@
"@types/react": "^19.2.0"
}
},
"node_modules/@types/ws": {
"version": "8.18.1",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@vapi-ai/web": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/@vapi-ai/web/-/web-2.5.2.tgz",
@@ -1981,6 +2077,15 @@
"node": ">= 14"
}
},
"node_modules/iceberg-js": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz",
"integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==",
"license": "MIT",
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+1
View File
@@ -13,6 +13,7 @@
"@google/genai": "^1.39.0",
"@google/generative-ai": "^0.24.1",
"@stripe/stripe-js": "^8.7.0",
"@supabase/supabase-js": "^2.97.0",
"@vapi-ai/web": "^2.5.2",
"chartjs-plugin-zoom": "^2.2.0",
"framer-motion": "^12.23.12",
+78
View File
@@ -0,0 +1,78 @@
-- Supabase Database Setup for SiteMente Mission Control
-- Run this in Supabase SQL Editor
-- Enable UUID extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Trading Journal Table
CREATE TABLE IF NOT EXISTS trades (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id TEXT DEFAULT 'default',
pair TEXT NOT NULL,
direction TEXT NOT NULL CHECK (direction IN ('long', 'short')),
entry_price NUMERIC,
exit_price NUMERIC,
status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'closed')),
is_demo BOOLEAN DEFAULT true,
trader_style TEXT,
setup TEXT,
timeframe TEXT,
pnl NUMERIC,
pnl_percent NUMERIC,
opened_at TIMESTAMPTZ DEFAULT NOW(),
closed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Tasks Table
CREATE TABLE IF NOT EXISTS tasks (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id TEXT DEFAULT 'default',
title TEXT NOT NULL,
description TEXT,
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'in_progress', 'completed')),
priority TEXT DEFAULT 'medium' CHECK (priority IN ('low', 'medium', 'high')),
due_date TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Leads/CRM Table
CREATE TABLE IF NOT EXISTS leads (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id TEXT DEFAULT 'default',
name TEXT NOT NULL,
business_name TEXT,
phone TEXT,
email TEXT,
source TEXT,
status TEXT NOT NULL DEFAULT 'new' CHECK (status IN ('new', 'contacted', 'qualified', 'won', 'lost')),
notes TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Enable RLS (Row Level Security) - optional for now
-- ALTER TABLE trades ENABLE ROW LEVEL SECURITY;
-- ALTER TABLE tasks ENABLE ROW LEVEL SECURITY;
-- ALTER TABLE leads ENABLE ROW LEVEL SECURITY;
-- Create indexes for better performance
CREATE INDEX IF NOT EXISTS idx_trades_user ON trades(user_id);
CREATE INDEX IF NOT EXISTS idx_trades_status ON trades(status);
CREATE INDEX IF NOT EXISTS idx_tasks_user ON tasks(user_id);
CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
CREATE INDEX IF NOT EXISTS idx_leads_user ON leads(user_id);
CREATE INDEX IF NOT EXISTS idx_leads_status ON leads(status);
-- Insert some sample data
INSERT INTO trades (pair, direction, entry_price, status, is_demo, trader_style, setup, timeframe) VALUES
('BTC/USD', 'long', 67500, 'open', true, 'thoth', 'Weekly structure break', '4H'),
('ETH/USD', 'long', 3200, 'open', true, 'dopetrades', 'Double bottom', '1H');
INSERT INTO tasks (title, description, status, priority) VALUES
('Fix Vapi integration', 'Get voice working on SiteMente', 'in_progress', 'high'),
('Contact local businesses', 'Reach out to leads in Benalmádena', 'pending', 'high'),
('Set up Supabase', 'Migrate from JSON to Supabase DB', 'in_progress', 'medium');
INSERT INTO leads (name, business_name, phone, status, source) VALUES
('Juan', 'Restaurante La Nina', '+34 952 449 193', 'new', 'cold_call'),
('Maria', 'Clínica Dental Málaga', '+34 951 123 456', 'contacted', 'website');