This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
ShopCall.ai is an AI-powered calling system integrated with e-commerce platforms. The project consists of:
/data/shopcall/
├── shopcall.ai-main/ # Frontend application
│ ├── src/
│ │ ├── components/ # React components
│ │ │ ├── context/ # React context providers (AuthContext)
│ │ │ └── ui/ # shadcn-ui component library
│ │ ├── pages/ # Route pages (Dashboard, Login, CallLogs, etc.)
│ │ ├── hooks/ # Custom React hooks
│ │ ├── lib/ # Utility functions
│ │ └── App.tsx # Main application with routing
│ └── package.json
└── supabase/ # Supabase Edge Functions
└── functions/ # Backend API functions
cd shopcall.ai-main
npm install # Install dependencies
npm run dev # Start dev server on port 8080
npm run build # Production build
npm run build:dev # Development build
npm run lint # Run ESLint
npm run preview # Preview production build
Supabase CLI Location: ~/bin/supabase (run from /home/claude/shopcall/supabase directory)
cd /home/claude/shopcall/supabase
# Deploy Edge Functions
~/bin/supabase functions deploy <function-name>
# Deploy with project ref
~/bin/supabase functions deploy <function-name> --project-ref ztklqodcdjeqpsvhlpud
# Test locally
~/bin/supabase functions serve
# Login (if needed)
~/bin/supabase login
# Link to project (if needed)
~/bin/supabase link --project-ref ztklqodcdjeqpsvhlpud
Authentication Flow:
src/components/context/AuthContext.tsx) manages global auth statesession_dataRouting Structure:
/ (Index) → Landing page
/signup → User registration
/login → User login
/otp → OTP verification
/dashboard (protected) → Main dashboard
/call-logs (protected) → Call history
/analytics (protected) → Analytics dashboard
/webshops (protected) → E-commerce integrations
/phone-numbers (protected) → Phone number management
/ai-config (protected) → AI configuration
/onboarding (protected) → User onboarding flow
/about, /privacy, /terms → Public pages
Component Organization:
src/pages/ handle routingDashboardContent.tsx) contain page logicsrc/components/ui/ are reusable shadcn componentsApp.tsxSupabase Edge Functions: Backend logic implemented as serverless Edge Functions
Authentication:
E-commerce Integrations:
oauth-shopify (OAuth 2.0 with HMAC verification)webhooks-shopify (customers/data_request, customers/redact, shop/redact)_shared/shopify-client.tsshopify-sync (manual sync for products, orders, customers)shopify-scheduled-sync (automated via pg_cron) - NOT YET IMPLEMENTEDoauth-woocommerce with action connect_manual_shared/woocommerce-client.tswoocommerce-sync (manual sync for products, orders, customers)woocommerce-scheduled-sync (automated via pg_cron, hourly)/wp-json/wc/v3/)/wc-api/v*) are deprecated and NOT supportedoauth-shoprenter-init, oauth-shoprenter-callbackwebhook-shoprenter-uninstallshoprenter-products, shoprenter-orders, shoprenter-customersshoprenter-syncshoprenter-scheduled-sync (automated via pg_cron, hourly)_shared/shoprenter-client.tsstores tablestores table:
- id: UUID (primary key)
- user_id: UUID (FK to profiles.id)
- platform_name: text (e.g., 'shopify', 'woocommerce', 'shoprenter')
- store_name: text
- store_url: text
- api_key: text (Consumer Key for WooCommerce, Access Token for ShopRenter/Shopify)
- api_secret: text (Consumer Secret for WooCommerce, Refresh Token for ShopRenter)
- access_token: text (for platforms using separate access_token field)
- refresh_token: text (for platforms using separate refresh_token field)
- token_expires_at: timestamp (token expiration time)
- scopes: text[]
- alt_data: jsonb (platform-specific data, e.g., expires_at, last_sync_at, wcVersion, wpVersion)
- connected_at: timestamp (when store was connected)
- is_active: boolean (default: true)
- phone_number: text
- package: text
- sync_status: text ('idle', 'syncing', 'completed', 'error')
- sync_started_at: timestamptz (when current/last sync started)
- sync_completed_at: timestamptz (when last sync completed)
- sync_error: text (error message from last failed sync)
- created_at: timestamp
- updated_at: timestamp
oauth_states table (for OAuth flow state management):
- id: UUID (primary key)
- state: text (UUID for CSRF protection, unique)
- user_id: UUID (FK to auth.users, nullable)
- platform: text (e.g., 'shopify', 'woocommerce', 'shoprenter')
- shopname: text (nullable)
- expires_at: timestamptz
- created_at: timestamptz
pending_shoprenter_installs table (temporary storage during OAuth):
- id: UUID (primary key)
- installation_id: text (UUID, unique)
- shopname: text
- access_token: text
- refresh_token: text (nullable)
- token_type: text (nullable, default: 'Bearer')
- expires_in: integer (nullable)
- scopes: text[] (nullable)
- expires_at: timestamptz
- created_at: timestamptz
oauth_nonces table (stores OAuth nonce values for OAuth flows):
- id: UUID (primary key)
- nonce: text (unique)
- user_id: UUID (FK to auth.users, nullable)
- platform: text
- shop: text (nullable)
- app_url: text (nullable)
- shopname: text (nullable)
- created_at: timestamptz
- expires_at: timestamptz
pending_signups table (stores pending signup data with OTP for email verification):
- id: UUID (primary key)
- signup_id: text (unique)
- email: text
- password: text
- full_name: text
- company_name: text
- user_name: text
- otp: text
- created_at: timestamptz
- expires_at: timestamptz (expires after 15 minutes)
profiles table (user profile information):
- id: UUID (primary key, FK to auth.users)
- full_name: text (nullable)
- username: text (nullable, unique)
- email: text (nullable)
- company_name: text (nullable)
- is_verified: boolean (nullable)
- created_at: timestamp (default: now())
- updated_at: timestamp (default: now())
shoprenter_products_cache table (cached product data):
- id: UUID (primary key)
- store_id: UUID (FK to stores)
- shoprenter_product_id: varchar
- product_data: jsonb
- last_synced_at: timestamptz
- created_at: timestamptz
- updated_at: timestamptz
shoprenter_tokens table (ShopRenter token management):
- id: UUID (primary key)
- store_id: UUID (FK to stores)
- access_token: text
- refresh_token: text (nullable)
- expires_at: timestamptz (nullable)
- scopes: text[] (nullable)
- shopname: varchar
- shop_domain: varchar
- is_active: boolean (default: true)
- last_sync_at: timestamptz (nullable)
- created_at: timestamptz
- updated_at: timestamptz
shoprenter_webhooks table (webhook registrations):
- id: UUID (primary key)
- store_id: UUID (FK to stores)
- webhook_id: varchar
- topic: varchar
- webhook_url: text
- is_active: boolean (default: true)
- created_at: timestamptz
- updated_at: timestamptz
woocommerce_products_cache table (cached WooCommerce products):
- id: UUID (primary key)
- store_id: UUID (FK to stores)
- wc_product_id: text
- name: text
- sku: text
- price: decimal
- currency: text
- description: text
- short_description: text
- stock_quantity: integer
- stock_status: text
- type: text ('simple', 'variable', 'grouped', 'external')
- categories: jsonb
- images: jsonb
- raw_data: jsonb
- last_synced_at: timestamptz
- created_at: timestamptz
- UNIQUE(store_id, wc_product_id)
woocommerce_orders_cache table (cached WooCommerce orders):
- id: UUID (primary key)
- store_id: UUID (FK to stores)
- wc_order_id: text
- order_number: text
- status: text
- total: decimal
- currency: text
- customer_name: text
- customer_email: text
- line_items: jsonb
- billing_address: jsonb
- shipping_address: jsonb
- created_at: timestamptz
- raw_data: jsonb
- last_synced_at: timestamptz
- UNIQUE(store_id, wc_order_id)
woocommerce_customers_cache table (cached WooCommerce customers):
- id: UUID (primary key)
- store_id: UUID (FK to stores)
- wc_customer_id: text
- email: text
- first_name: text
- last_name: text
- username: text
- billing_address: jsonb
- shipping_address: jsonb
- orders_count: integer
- total_spent: decimal
- raw_data: jsonb
- last_synced_at: timestamptz
- created_at: timestamptz
- UNIQUE(store_id, wc_customer_id)
shopify_products_cache table (cached Shopify products):
- id: UUID (primary key)
- store_id: UUID (FK to stores)
- shopify_product_id: text
- title: text
- handle: text
- vendor: text
- product_type: text
- status: text
- price: decimal
- compare_at_price: decimal
- currency: text
- sku: text
- inventory_quantity: integer
- description: text
- images: jsonb
- variants: jsonb
- options: jsonb
- tags: text[]
- raw_data: jsonb
- last_synced_at: timestamptz
- created_at: timestamptz
- UNIQUE(store_id, shopify_product_id)
shopify_orders_cache table (cached Shopify orders):
- id: UUID (primary key)
- store_id: UUID (FK to stores)
- shopify_order_id: text
- order_number: text
- name: text (e.g., "#1001")
- email: text
- phone: text
- financial_status: text
- fulfillment_status: text
- total_price: decimal
- subtotal_price: decimal
- total_tax: decimal
- currency: text
- customer_name: text
- customer_email: text
- line_items: jsonb
- billing_address: jsonb
- shipping_address: jsonb
- note: text
- tags: text[]
- order_created_at: timestamptz
- order_updated_at: timestamptz
- raw_data: jsonb
- last_synced_at: timestamptz
- created_at: timestamptz
- UNIQUE(store_id, shopify_order_id)
shopify_customers_cache table (cached Shopify customers):
- id: UUID (primary key)
- store_id: UUID (FK to stores)
- shopify_customer_id: text
- email: text
- first_name: text
- last_name: text
- phone: text
- accepts_marketing: boolean
- orders_count: integer
- total_spent: decimal
- currency: text
- state: text (enabled, disabled, declined, invited)
- addresses: jsonb
- default_address: jsonb
- tags: text[]
- note: text
- customer_created_at: timestamptz
- customer_updated_at: timestamptz
- raw_data: jsonb
- last_synced_at: timestamptz
- created_at: timestamptz
- UNIQUE(store_id, shopify_customer_id)
gdpr_requests table (GDPR compliance tracking):
- id: UUID (primary key)
- store_id: UUID (FK to stores)
- request_type: text ('data_request', 'customer_redact', 'shop_redact')
- customer_id: text
- shop_domain: text
- request_payload: jsonb
- status: text ('pending', 'processing', 'completed', 'failed')
- processed_at: timestamptz
- error_message: text
- created_at: timestamptz
- updated_at: timestamptz
shopify_webhooks table (webhook registration tracking):
- id: UUID (primary key)
- store_id: UUID (FK to stores)
- shopify_webhook_id: text
- topic: text (e.g., 'orders/create', 'products/update')
- address: text (webhook callback URL)
- format: text (default: 'json')
- is_active: boolean (default: true)
- last_received_at: timestamptz (nullable)
- total_received: integer (default: 0)
- created_at: timestamptz
- updated_at: timestamptz
- UNIQUE(store_id, shopify_webhook_id)
system_config table (system-wide configuration):
- key: text (primary key)
- value: text
- description: text (nullable)
- created_at: timestamptz
- updated_at: timestamptz
call_logs table (call history and analytics):
- id: UUID (primary key)
- user_id: UUID (FK to auth.users, nullable)
- contact_id: UUID (nullable)
- workspace_id: UUID
- status: call_status_enum (pending, scheduled, in_progress, completed, failed, cancelled)
- attempt_number: integer
- scheduled_at: timestamptz (nullable)
- started_at: timestamptz (nullable)
- ended_at: timestamptz (nullable)
- duration_seconds: integer (nullable)
- transcript: text (nullable)
- summary: text (nullable)
- call_outcome: call_outcome_enum (pending, resolved, interested, potential, not_interested, no_answer, voicemail, busy, callback_requested, false)
- assistant_id: text (nullable)
- assistant_name: text (nullable)
- callprovider_id: text (nullable)
- phone_number_id: text (nullable)
- customer_number: text (nullable)
- assistant_phone_number: text (nullable)
- call_type: call_type_enum (inbound, outbound, inbound_dummy)
- cost_stt: numeric (nullable)
- cost_llm: numeric (nullable)
- cost_tts: numeric (nullable)
- cost_total: numeric (nullable)
- cost_twilio: numeric (nullable)
- cost_totals: numeric (nullable)
- ended_reason: text (nullable)
- recording_url: text (nullable)
- created_at: timestamptz
- updated_at: timestamptz
sync_logs table (scheduled sync execution logs):
- id: UUID (primary key)
- sync_type: text ('manual', 'scheduled', 'webhook')
- platform: text ('shopify', 'woocommerce', 'shoprenter')
- stores_processed: integer
- results: jsonb (detailed results per store)
- started_at: timestamptz
- completed_at: timestamptz
- created_at: timestamptz
store_sync_config table (per-store sync configuration):
- id: UUID (primary key)
- store_id: UUID (FK to stores, unique)
- enabled: boolean (default: true)
- sync_frequency: text ('15min', '30min', 'hourly', '6hours', 'daily')
- last_sync_at: timestamptz
- next_sync_at: timestamptz (auto-calculated)
- products_access_policy: data_access_policy enum ('sync', 'api_only', 'not_allowed') - GDPR-compliant access control
- customers_access_policy: data_access_policy enum ('sync', 'api_only', 'not_allowed') - GDPR-compliant access control
- orders_access_policy: data_access_policy enum ('sync', 'api_only', 'not_allowed') - GDPR-compliant access control
- created_at: timestamptz
- updated_at: timestamptz
shopcall.ai-main/.env.example to .env and fill in your valuessupabase/.env.example to .env and fill in your values.env (shopcall.ai-main)Required environment variables:
# Supabase Configuration (from Supabase Dashboard → Settings → API)
VITE_SUPABASE_URL=https://YOUR_PROJECT.supabase.co
VITE_SUPABASE_ANON_KEY=your_supabase_anon_key
# Backend API Base URL (typically VITE_SUPABASE_URL + /functions/v1)
VITE_API_URL=https://YOUR_PROJECT.supabase.co/functions/v1
# Frontend URL (for OAuth callbacks)
VITE_FRONTEND_URL=http://localhost:8080 # or https://yourdomain.com for production
# GDPR Compliance Settings (optional)
# Hide data access policy settings from store owners for stricter GDPR compliance
# When set to 'true', the corresponding settings will be hidden in the UI
VITE_HIDE_ORDERS_ACCESS_SETTINGS=true # Hide orders access policy settings
VITE_HIDE_CUSTOMERS_ACCESS_SETTINGS=true # Hide customers access policy settings
Note: VITE_API_URL is derived from VITE_SUPABASE_URL by appending /functions/v1
GDPR Settings: When VITE_HIDE_ORDERS_ACCESS_SETTINGS and VITE_HIDE_CUSTOMERS_ACCESS_SETTINGS are set to true, store owners won't see these settings in the UI, and new stores will default to api_only mode (direct API access, no local caching of PII).
.env (supabase)Required environment variables:
# Supabase Configuration (from Supabase Dashboard → Settings → API)
SUPABASE_URL=https://YOUR_PROJECT.supabase.co
SUPABASE_ANON_KEY=your_supabase_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key
# Frontend URL (same as VITE_FRONTEND_URL in frontend .env)
FRONTEND_URL=http://localhost:8080 # or https://yourdomain.com for production
# E-commerce Platform OAuth Credentials
SHOPIFY_API_KEY=your_shopify_api_key
SHOPIFY_API_SECRET=your_shopify_api_secret
SHOPRENTER_CLIENT_ID=your_shoprenter_client_id
SHOPRENTER_CLIENT_SECRET=your_shoprenter_client_secret
# Internal Security (generate with: openssl rand -hex 32)
INTERNAL_SYNC_SECRET=your_random_secure_secret
# Email Configuration (from https://resend.com/api-keys)
RESEND_API_KEY=your_resend_api_key
CRITICAL: Configure in Supabase Dashboard → Project Settings → Database → Custom Postgres Configuration:
app.internal_sync_secret = 'same_as_INTERNAL_SYNC_SECRET_above'
app.supabase_url = 'https://YOUR_PROJECT.supabase.co'
Why this is required: The pg_cron jobs run database-side trigger functions that need to make HTTP requests to Edge Functions. These settings allow the trigger functions to authenticate and call the scheduled sync Edge Functions.
Without these settings:
To verify settings are configured:
SELECT
current_setting('app.internal_sync_secret', true) as internal_secret,
current_setting('app.supabase_url', true) as supabase_url;
Both values should be non-NULL.
npm run build generates static files in dist/supabase functions deploy commandIMPORTANT: When working with Supabase in Claude Code, ALWAYS use the Supabase MCP tools instead of CLI commands. These tools provide direct integration with Supabase without requiring manual authentication.
Edge Functions Management:
mcp__supabase__list_edge_functions - List all deployed Edge Functionsmcp__supabase__deploy_edge_function - Deploy an Edge Function (with files array)mcp__supabase__get_edge_function - Get Edge Function codeDatabase Operations:
mcp__supabase__list_tables - List database tablesmcp__supabase__list_extensions - List database extensionsmcp__supabase__list_migrations - List all migrationsmcp__supabase__apply_migration - Apply a new migration (DDL operations)mcp__supabase__execute_sql - Execute raw SQL queriesProject Information:
mcp__supabase__get_project_url - Get the project API URLmcp__supabase__get_anon_key - Get the anonymous API keymcp__supabase__generate_typescript_types - Generate TypeScript types from databaseMonitoring & Debugging:
mcp__supabase__get_logs - Get logs for Edge Functions or other servicesmcp__supabase__get_advisors - Check for security/performance issuesBranching (Development):
mcp__supabase__create_branch - Create a development branchmcp__supabase__list_branches - List all branchesmcp__supabase__merge_branch - Merge branch to productionmcp__supabase__delete_branch - Delete a development branchCheck Edge Function logs when debugging deployment issues:
mcp__supabase__get_logs(service: "edge-function")
Monitor deployments by listing Edge Functions after deployment
Environment variables are configured via .env files in both frontend and supabase directories
For production deployment, ensure both .env files are properly configured
Deploying Edge Functions via CLI:
cd /home/claude/shopcall/supabase
# Deploy a specific function
~/bin/supabase functions deploy <function-name> --project-ref ztklqodcdjeqpsvhlpud
# Deploy multiple functions
~/bin/supabase functions deploy api oauth-woocommerce oauth-shopify --project-ref ztklqodcdjeqpsvhlpud
Checking Deployment Status:
Use the MCP tool mcp__supabase__list_edge_functions to verify deployment status and version numbers.
Debugging 404/500 Errors:
mcp__supabase__list_edge_functionsmcp__supabase__get_logs(service: "edge-function")supabase/.envWhen making changes:
shopcall.ai-main/src/supabase/functions/<PrivateRoute> wrappersupabase/functions/IMPORTANT: After modifying any Supabase Edge Function code, you MUST redeploy the function to Supabase for the changes to take effect!
Steps to deploy Edge Functions:
Via MCP Tools (PREFERRED for Claude Code):
Use mcp__supabase__deploy_edge_function with:
- name: function name (e.g., "trigger-sync", "api", "oauth-shopify")
- files: array of files with name and content properties
Example:
mcp__supabase__deploy_edge_function(
name: "trigger-sync",
files: [{"name": "index.ts", "content": "..."}]
)
Benefits: No manual authentication, automatic project selection, works directly in Claude Code
Via CLI (alternative, requires manual authentication):
cd /home/claude/shopcall/supabase
# Deploy a specific function
~/bin/supabase functions deploy <function-name> --project-ref ztklqodcdjeqpsvhlpud
# Deploy multiple functions at once
~/bin/supabase functions deploy oauth-woocommerce oauth-shopify api --project-ref ztklqodcdjeqpsvhlpud
Verify deployment:
mcp__supabase__list_edge_functionsmcp__supabase__get_logs(service: "edge-function")Common mistake: Forgetting to redeploy after code changes means the old version continues running in production!
Which functions to deploy:
oauth-woocommerce/index.ts), deploy that function_shared/woocommerce-client.ts), deploy ALL functions that import itstores tableFrontend uses TypeScript path alias:
@/ → ./src/Example: import { Button } from "@/components/ui/button"
The project includes automated background sync capabilities for ShopRenter and WooCommerce using PostgreSQL's pg_cron extension. Shopify scheduled sync is prepared in the migration but not yet implemented.
How it works:
sync_logs tableImplemented Scheduled Syncs:
shoprenter-scheduled-sync (runs hourly at minute 0)woocommerce-scheduled-sync (runs hourly at minute 5)Sync Frequencies (configurable per store in store_sync_config):
15min - Every 15 minutes (high-frequency updates)30min - Every 30 minuteshourly - Every hour (default, recommended)6hours - Every 6 hoursdaily - Once per dayConfiguration:
store_sync_config tableMonitoring:
sync_logs table tracks all sync executionssync_status, sync_started_at, sync_completed_at, sync_error fieldsSetup Requirements:
supabase/migrations/20250129_shoprenter_scheduled_sync.sqlsupabase/migrations/20251030_woocommerce_scheduled_sync.sqlsupabase/migrations/20251030_shopify_integration.sqlsupabase functions deploy shoprenter-scheduled-syncsupabase functions deploy woocommerce-scheduled-syncsupabase functions deploy shopify-scheduled-sync (when implemented)INTERNAL_SYNC_SECRET in Edge Functions environmentsync_logs table for execution resultsManual Control:
-- Enable/disable sync for a store
SELECT set_store_sync_enabled('store-uuid', true);
-- Change sync frequency
SELECT set_store_sync_frequency('store-uuid', 'hourly');
-- View recent sync logs
SELECT * FROM sync_logs ORDER BY created_at DESC LIMIT 10;
-- View sync statistics (if view exists)
SELECT * FROM sync_statistics;
-- Check current pg_cron jobs
SELECT * FROM cron.job;
Security:
INTERNAL_SYNC_SECRET prevents unauthorized sync triggersTrigger Functions:
trigger_shoprenter_scheduled_sync() - Triggers ShopRenter synctrigger_woocommerce_scheduled_sync() - Triggers WooCommerce synctrigger_shopify_scheduled_sync() - Triggers Shopify sync (when implemented)