diff --git a/src/app/Http/Controllers/AdminController.php b/src/app/Http/Controllers/AdminController.php index 86248ee..052c010 100644 --- a/src/app/Http/Controllers/AdminController.php +++ b/src/app/Http/Controllers/AdminController.php @@ -27,6 +27,10 @@ public function dashboardSettings($dashboard) ['key' => 'woo_store_url', 'name' => 'WooCommerce Store URL', 'type' => 'string', 'default' => 'https://example.com', 'description' => 'The base URL of your WooCommerce store (e.g. https://shop.stargas.co.za).'], ['key' => 'woo_consumer_key', 'name' => 'Consumer Key', 'type' => 'string', 'default' => '', 'description' => 'WooCommerce REST API Consumer Key (ck_...).'], ['key' => 'woo_consumer_secret', 'name' => 'Consumer Secret', 'type' => 'string', 'default' => '', 'description' => 'WooCommerce REST API Consumer Secret (cs_...).'], + ['key' => 'fb_app_id', 'name' => 'Facebook App ID', 'type' => 'string', 'default' => '', 'description' => 'Your Facebook Application ID.'], + ['key' => 'fb_app_secret', 'name' => 'Facebook App Secret', 'type' => 'string', 'default' => '', 'description' => 'Your Facebook Application Secret.'], + ['key' => 'fb_access_token', 'name' => 'Facebook Access Token', 'type' => 'string', 'default' => '', 'description' => 'Long-lived Facebook Access Token for API calls.'], + ['key' => 'fb_ad_account_id', 'name' => 'Facebook Ad Account ID', 'type' => 'string', 'default' => '', 'description' => 'Your Facebook Ad Account ID (e.g. act_123456789).'], ]; } else { abort(404); diff --git a/src/app/Http/Controllers/EcommerceController.php b/src/app/Http/Controllers/EcommerceController.php index da876c6..c418176 100644 --- a/src/app/Http/Controllers/EcommerceController.php +++ b/src/app/Http/Controllers/EcommerceController.php @@ -371,4 +371,127 @@ public function insights(Request $request) return response()->json(['error' => 'Failed to fetch WooCommerce insights data.'], 500); } } + } + + /** + * Marketing & ROI (Facebook Ads + WooCommerce) + */ + public function marketing(Request $request) + { + $fbAppId = Setting::getValue('ecommerce', 'fb_app_id'); + $fbToken = Setting::getValue('ecommerce', 'fb_access_token'); + $fbAccountId = Setting::getValue('ecommerce', 'fb_ad_account_id'); + + if (!$fbToken || !$fbAccountId) { + return response()->json(['error' => 'Facebook credentials not configured in Admin settings.', 'configured' => false], 200); + } + + // Base WooCommerce Sales calculation to determine ROAS + $client = $this->wooClient(); + $netSales = 0; + + [$from, $to] = $this->dateRange($request); + + if ($client) { + $params = []; + if ($from) $params['date_min'] = $from; + if ($to) $params['date_max'] = $to; + + try { + $salesResp = $client->get('reports/sales', $params); + if ($salesResp->successful()) { + $salesData = $salesResp->json(); + if (!empty($salesData)) { + $netSales = (float) ($salesData[0]['net_sales'] ?? 0); + } + } + } catch (\Exception $e) { + // Silently drop woo error mapped to marketing + } + } + + // Facebook Date Filtering + $fbParams = [ + 'level' => 'account', + 'fields' => 'spend,impressions,clicks,cpc,cpa', + ]; + + if ($from && $to) { + $fbParams['time_range'] = json_encode(['since' => $from, 'until' => $to]); + } else { + // Default to last 30 days if no explicit date range is given + $fbParams['date_preset'] = 'last_30d'; + } + + try { + // Remove 'act_' prefix if user included it, then add it back to be safe + $accountId = str_replace('act_', '', $fbAccountId); + $fbUrl = "https://graph.facebook.com/v19.0/act_{$accountId}"; + + // 1. Fetch Account-level Insights + $fbResp = Http::withToken($fbToken)->get("{$fbUrl}/insights", $fbParams); + + $spend = 0; + $impressions = 0; + $clicks = 0; + + if ($fbResp->successful()) { + $fbData = $fbResp->json(); + if (isset($fbData['data']) && count($fbData['data']) > 0) { + $insight = $fbData['data'][0]; + $spend = (float) ($insight['spend'] ?? 0); + $impressions = (int) ($insight['impressions'] ?? 0); + $clicks = (int) ($insight['clicks'] ?? 0); + } + } + + // 2. Fetch Top Campaigns by Spend + $campParams = [ + 'level' => 'campaign', + 'fields' => 'campaign_name,spend,clicks,impressions', + 'sort' => ['spend_descending'], + 'limit' => 5 + ]; + if ($from && $to) { + $campParams['time_range'] = json_encode(['since' => $from, 'until' => $to]); + } else { + $campParams['date_preset'] = 'last_30d'; + } + + $campResp = Http::withToken($fbToken)->get("{$fbUrl}/insights", $campParams); + $campaigns = []; + if ($campResp->successful()) { + $campData = $campResp->json(); + if (isset($campData['data'])) { + foreach ($campData['data'] as $cmp) { + $campaigns[] = [ + 'name' => $cmp['campaign_name'] ?? 'Unknown', + 'spend' => (float) ($cmp['spend'] ?? 0), + 'clicks' => (int) ($cmp['clicks'] ?? 0), + 'impressions' => (int) ($cmp['impressions'] ?? 0), + ]; + } + } + } + + // Calculate ROAS / CPC + $roas = $spend > 0 ? ($netSales / $spend) : 0; + $cpc = $clicks > 0 ? ($spend / $clicks) : 0; + + return response()->json([ + 'configured' => true, + 'spend' => $spend, + 'impressions' => $impressions, + 'clicks' => $clicks, + 'cpc' => $cpc, + 'roas' => $roas, + 'woo_net_sales' => $netSales, + 'campaigns' => $campaigns + ]); + + } catch (\Exception $e) { + Log::error('Facebook API Error: ' . $e->getMessage()); + return response()->json(['error' => 'Failed to fetch Facebook data.'], 500); + } + } } diff --git a/src/resources/views/admin/index.blade.php b/src/resources/views/admin/index.blade.php index 05704b1..ecf9df6 100644 --- a/src/resources/views/admin/index.blade.php +++ b/src/resources/views/admin/index.blade.php @@ -21,7 +21,7 @@
Configure WooCommerce API credentials and integration parameters for the online store dashboard.
+Configure WooCommerce API and Facebook Business credentials for the marketing dashboard.
diff --git a/src/resources/views/ecommerce.blade.php b/src/resources/views/ecommerce.blade.php index a8620ec..f6bb749 100644 --- a/src/resources/views/ecommerce.blade.php +++ b/src/resources/views/ecommerce.blade.php @@ -182,6 +182,87 @@ + +