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
# Deploy Edge Functions
supabase functions deploy <function-name>
# Test locally
supabase functions serve
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)oauth-woocommerce (OAuth 1.0a authentication)_shared/woocommerce-client.tswoocommerce-sync (manual sync for products, orders, customers)oauth-shoprenter-init, oauth-shoprenter-callbackwebhook-shoprenter-uninstallshoprenter-products, shoprenter-orders, shoprenter-customersshoprenter-syncshoprenter-scheduled-sync (automated via pg_cron)_shared/shoprenter-client.tsstores tablestores table:
- user_id: UUID (FK to auth.users)
- platform_name: text (e.g., 'shopify', 'woocommerce', 'shoprenter')
- store_name: text
- store_url: text
- api_key: text (access token for ShopRenter)
- api_secret: text (refresh token for ShopRenter)
- scopes: text[]
- alt_data: jsonb (platform-specific data, e.g., expires_at, last_sync_at)
- phone_number: text
- package: text
oauth_states table (for OAuth flow state management):
- state: text (UUID for CSRF protection)
- user_id: UUID (FK to auth.users)
- platform: text (e.g., 'shoprenter')
- shopname: text
- expires_at: timestamp
pending_shoprenter_installs table (temporary storage during OAuth):
- installation_id: text (UUID)
- shopname: text
- access_token: text
- refresh_token: text
- token_type: text
- expires_in: integer
- scopes: text[]
- expires_at: timestamp
shoprenter_products_cache table (cached product data):
- store_id: UUID (FK to stores)
- shoprenter_product_id: text
- name: text
- sku: text
- price: decimal
- currency: text
- description: text
- stock: integer
- active: boolean
- raw_data: jsonb
- last_synced_at: timestamp
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
- last_received_at: timestamptz
- total_received: integer
- created_at: timestamptz
- updated_at: timestamptz
- UNIQUE(store_id, shopify_webhook_id)
shoprenter_webhooks table (webhook registrations):
- store_id: UUID (FK to stores)
- shoprenter_webhook_id: text
- event: text (e.g., 'order/create')
- callback_url: text
- is_active: boolean
- last_received_at: timestamp
- total_received: integer
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)
- sync_products: boolean (default: true)
- sync_orders: boolean (default: true)
- sync_customers: boolean (default: true)
- 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
Note: VITE_API_URL is derived from VITE_SUPABASE_URL by appending /functions/v1
.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
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'
npm run build generates static files in dist/supabase functions deploy commandWhen making changes:
shopcall.ai-main/src/supabase/functions/<PrivateRoute> wrappersupabase/functions/stores tableFrontend uses TypeScript path alias:
@/ → ./src/Example: import { Button } from "@/components/ui/button"
The ShopRenter integration includes automated background sync capabilities using PostgreSQL's pg_cron extension.
How it works:
sync_logs tableSync Frequencies:
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_statistics view provides aggregated metricsSetup Requirements:
supabase/migrations/20250129_shoprenter_scheduled_sync.sqlsupabase functions deploy shoprenter-scheduled-syncINTERNAL_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
SELECT * FROM sync_statistics;
Security:
INTERNAL_SYNC_SECRET prevents unauthorized sync triggers