CLAUDE.md 16 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

ShopCall.ai is an AI-powered calling system integrated with e-commerce platforms. The project consists of:

  • shopcall.ai-main: React/Vite frontend application with TypeScript
  • supabase: Supabase Edge Functions for backend API

Repository Structure

/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

Development Commands

Frontend (shopcall.ai-main)

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 Edge Functions

# Deploy Edge Functions
supabase functions deploy <function-name>

# Test locally
supabase functions serve

Technology Stack

Frontend

  • Framework: React 18 with Vite
  • Language: TypeScript
  • UI Library: shadcn-ui with Radix UI primitives
  • Styling: Tailwind CSS
  • Routing: React Router v6
  • State Management: React Query (@tanstack/react-query)
  • Form Handling: React Hook Form with Zod validation
  • Theme: next-themes for dark mode support

Backend

  • Platform: Supabase Edge Functions (Deno)
  • Database: Supabase (PostgreSQL with auth)
  • Authentication: Supabase Auth

Architecture

Frontend Architecture

Authentication Flow:

  • AuthContext provider (src/components/context/AuthContext.tsx) manages global auth state
  • Token stored in localStorage as session_data
  • PrivateRoute component protects authenticated routes
  • Auth validation via Supabase client

Routing 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:

  • Page components in src/pages/ handle routing
  • Content components (e.g., DashboardContent.tsx) contain page logic
  • UI components in src/components/ui/ are reusable shadcn components
  • Context providers wrap the app in App.tsx

Backend Architecture

Supabase Edge Functions: Backend logic implemented as serverless Edge Functions

Authentication:

  • Handled by Supabase Auth
  • User management and session handling via Supabase client

E-commerce Integrations:

  • Shopify integration (fully implemented)
    • OAuth flow: oauth-shopify (OAuth 2.0 with HMAC verification)
    • GDPR webhooks: webhooks-shopify (customers/data_request, customers/redact, shop/redact)
    • API client: _shared/shopify-client.ts
    • Data synchronization: shopify-sync (manual sync for products, orders, customers)
    • Read-only access with scopes: read_products, read_orders, read_customers, read_inventory, read_price_rules
    • Secure OAuth 2.0 with HMAC-SHA256 signature validation
    • Rate limiting (5 req/sec) and retry logic with exponential backoff
    • Automatic pagination support for all data types
    • Shopify App Store compliant - includes mandatory GDPR webhooks
  • WooCommerce integration (fully implemented)
    • OAuth flow: oauth-woocommerce (OAuth 1.0a authentication)
    • API client: _shared/woocommerce-client.ts
    • Data synchronization: woocommerce-sync (manual sync for products, orders, customers)
    • Read-only access to products, orders, and customers
    • Secure OAuth 1.0a with HMAC-SHA256 signatures
    • Rate limiting (5 req/sec) and retry logic with exponential backoff
    • Pagination support for large datasets
  • ShopRenter integration (fully implemented)
    • OAuth flow: oauth-shoprenter-init, oauth-shoprenter-callback
    • Uninstall webhook: webhook-shoprenter-uninstall
    • Data sync endpoints: shoprenter-products, shoprenter-orders, shoprenter-customers
    • Manual sync: shoprenter-sync
    • Scheduled background sync: shoprenter-scheduled-sync (automated via pg_cron)
    • API client: _shared/shoprenter-client.ts
  • Store credentials in Supabase stores table

Database Schema (Supabase)

stores 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

Environment Configuration

Frontend .env (shopcall.ai-main)

# Supabase Configuration
VITE_SUPABASE_URL=https://ztklqodcdjeqpsvhlpud.supabase.co
VITE_SUPABASE_ANON_KEY=<anon_key>

# Backend API Base URL (Supabase Edge Functions)
VITE_API_URL=https://ztklqodcdjeqpsvhlpud.supabase.co/functions/v1

# Frontend URL (for OAuth callbacks)
VITE_FRONTEND_URL=https://shopcall.ai

Supabase Edge Functions .env

# OAuth Configuration
SHOPIFY_API_KEY=<shopify_api_key>
SHOPIFY_API_SECRET=<shopify_api_secret>
SHOPRENTER_CLIENT_ID=<shoprenter_client_id>
SHOPRENTER_CLIENT_SECRET=<shoprenter_client_secret>

# Scheduled Sync Security
INTERNAL_SYNC_SECRET=<random_secure_secret_for_scheduled_sync>

# Email Configuration
RESEND_API_KEY=<resend_api_key>

# Frontend URL
FRONTEND_URL=https://shopcall.ai

# Supabase Configuration
SUPABASE_URL=<supabase_project_url>
SUPABASE_ANON_KEY=<supabase_anon_key>
SUPABASE_SERVICE_ROLE_KEY=<supabase_service_role_key>

Supabase Database Settings (for pg_cron)

Configure in Supabase Dashboard → Project Settings → Database → Custom Postgres Configuration:

app.internal_sync_secret = '<same_as_INTERNAL_SYNC_SECRET_above>'
app.supabase_url = 'https://ztklqodcdjeqpsvhlpud.supabase.co'

Deployment

Frontend

  • Build: npm run build generates static files in dist/
  • Deployment: Can be deployed to any static hosting service (Netlify, Vercel, GitHub Pages, etc.)
  • SPA routing handled by React Router

Supabase Edge Functions

  • Deployment: Use supabase functions deploy command
  • Edge Functions are deployed to Supabase infrastructure
  • Automatically scaled and managed by Supabase

Development Workflow

When making changes:

  1. Frontend Changes: Work in shopcall.ai-main/src/
  2. Backend Changes: Modify Supabase Edge Functions in supabase/functions/
  3. Auth Flow Changes: Update AuthContext and Supabase Auth configuration
  4. New Protected Routes: Add to App.tsx inside <PrivateRoute> wrapper
  5. New API Endpoints: Create new Edge Functions in supabase/functions/

Important Notes

  • Backend is implemented as Supabase Edge Functions (serverless)
  • Supabase handles user authentication, data storage, and API endpoints
  • Frontend is a static React application that can be hosted anywhere
  • All API calls go through Supabase Edge Functions
  • OAuth flows store credentials in Supabase stores table

Path Aliases

Frontend uses TypeScript path alias:

  • @/./src/

Example: import { Button } from "@/components/ui/button"

Background Sync & Scheduling

ShopRenter Automated Synchronization

The ShopRenter integration includes automated background sync capabilities using PostgreSQL's pg_cron extension.

How it works:

  1. pg_cron schedules database jobs at specified intervals
  2. pg_net makes HTTP requests from the database to Edge Functions
  3. shoprenter-scheduled-sync Edge Function processes all active stores
  4. Sync results are logged to sync_logs table

Sync Frequencies:

  • 15min - Every 15 minutes (high-frequency updates)
  • 30min - Every 30 minutes
  • hourly - Every hour (default, recommended)
  • 6hours - Every 6 hours
  • daily - Once per day

Configuration:

  • Per-store sync frequency via store_sync_config table
  • Enable/disable sync per store
  • Choose what to sync (products, orders, customers)
  • Automatic next sync calculation

Monitoring:

  • sync_logs table tracks all sync executions
  • sync_statistics view provides aggregated metrics
  • Logs include per-store success/failure status
  • Sync duration and item counts tracked

Setup Requirements:

  1. Run migration: supabase/migrations/20250129_shoprenter_scheduled_sync.sql
  2. Deploy Edge Function: supabase functions deploy shoprenter-scheduled-sync
  3. Configure INTERNAL_SYNC_SECRET in Edge Functions environment
  4. Configure database settings in Supabase Dashboard
  5. Monitor sync_logs table for execution results

Manual 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
  • Only internal pg_cron jobs can trigger scheduled sync
  • Row-level security ensures users only see their own sync logs