# 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) ```bash 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 ```bash # Deploy Edge Functions supabase functions deploy # 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 ### Quick Setup 1. **Frontend**: Copy `shopcall.ai-main/.env.example` to `.env` and fill in your values 2. **Backend**: Copy `supabase/.env.example` to `.env` and fill in your values 3. **Database**: Configure pg_cron settings in Supabase Dashboard (see below) ### Frontend `.env` (shopcall.ai-main) Required environment variables: ```bash # 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` ### Supabase Edge Functions `.env` (supabase) Required environment variables: ```bash # 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 ``` ### 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://YOUR_PROJECT.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 `` 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**: ```sql -- 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