hasTable('mod_fusionpbx_dids')) { // Get unique provinces $provinces = Capsule::table('mod_fusionpbx_dids') ->distinct() ->whereNotNull('province') ->where('province', '!=', '') ->orderBy('province') ->pluck('province') ->toArray(); // Get unique cities $cities = Capsule::table('mod_fusionpbx_dids') ->distinct() ->whereNotNull('city') ->where('city', '!=', '') ->orderBy('city') ->pluck('city') ->toArray(); } } catch (Exception $e) { // Log error but continue logActivity("FusionPBX Config Options Error: " . $e->getMessage()); } // If no provinces in database, use default South African provinces if (empty($provinces)) { $provinces = [ 'Gauteng', 'Western Cape', 'KwaZulu-Natal', 'Eastern Cape', 'Free State', 'Limpopo', 'Mpumalanga', 'North West', 'Northern Cape' ]; } // If no cities in database, use empty array if (empty($cities)) { $cities = []; } $provinceOptions = implode(',', $provinces); $cityOptions = implode(',', $cities); return [ // Domain Configuration "Domain Name" => [ "Type" => "text", "Size" => "50", "Description" => "Custom domain name for your PBX (leave empty for auto-generation)", "Default" => "" ], // Extension Configuration "Primary Extension" => [ "Type" => "text", "Size" => "10", "Description" => "Primary extension number for this account", "Default" => "1000" ], "Extension Password" => [ "Type" => "text", "Size" => "20", "Description" => "Password for extension login (leave empty for auto-generation)", "Default" => "" ], // DID Configuration "DID Province" => [ "Type" => "dropdown", "Options" => $provinceOptions, "Description" => "Select province for DID assignment", "Default" => !empty($provinces) ? $provinces[0] : "Gauteng" ], "DID City" => [ "Type" => "text", "Size" => "30", "Description" => "Enter city name for DID assignment (will suggest available cities)", "Default" => "" ], "DID Number" => [ "Type" => "text", "Size" => "20", "Description" => "Specific DID number to assign (leave empty for auto-assignment from selected province/city)", "Default" => "" ], // IVR Configuration "Create IVR" => [ "Type" => "dropdown", "Options" => "No,Yes", "Description" => "Create a default IVR menu for this tenant", "Default" => "Yes" ], "IVR Name" => [ "Type" => "text", "Size" => "30", "Description" => "Name for the IVR menu", "Default" => "Main IVR" ], "IVR Extension" => [ "Type" => "text", "Size" => "10", "Description" => "Extension number for the IVR", "Default" => "5000" ], // AI Receptionist Configuration "AI Receptionist" => [ "Type" => "dropdown", "Options" => "No,Yes", "Description" => "Enable AI Receptionist for this service", "Default" => "No" ], "AI Credits" => [ "Type" => "text", "Size" => "5", "Description" => "Initial AI credits to allocate", "Default" => "50" ], "AI Voice" => [ "Type" => "dropdown", "Options" => "alloy,echo,fable,onyx,nova,shimmer", "Description" => "Voice to use for AI Receptionist", "Default" => "alloy" ], "AI Language" => [ "Type" => "dropdown", "Options" => "en,af,zu,xh,nso,tn,st,ts,ss,ve", "Description" => "Language for AI Receptionist", "Default" => "en" ], "AI Fallback Extension" => [ "Type" => "text", "Size" => "10", "Description" => "Fallback extension when AI cannot handle call", "Default" => "1000" ], "AI Prompt Text" => [ "Type" => "textarea", "Rows" => "4", "Cols" => "50", "Description" => "Custom system prompt for AI behavior" ], // FUP Configuration "FUP Limit (minutes)" => [ "Type" => "text", "Size" => "5", "Description" => "Fair Usage Policy limit in minutes per month", "Default" => "1000" ], "FUP Action" => [ "Type" => "dropdown", "Options" => "Suspend,Notify Only,Charge Overage", "Description" => "Action when FUP limit is exceeded", "Default" => "Notify Only" ] ]; } /** * Add JavaScript for dynamic city suggestions */ add_hook('ClientAreaPageCart', 1, function($vars) { if ($vars['templatefile'] == 'configureproduct') { // Get available cities from database for JavaScript $cities = []; try { if (Capsule::schema()->hasTable('mod_fusionpbx_dids')) { $cities = Capsule::table('mod_fusionpbx_dids') ->distinct() ->whereNotNull('city') ->where('city', '!=', '') ->orderBy('city') ->pluck('city') ->toArray(); } } catch (Exception $e) { // Silent fail - cities array will be empty } $citiesJson = json_encode($cities); return << document.addEventListener('DOMContentLoaded', function() { const cities = {$citiesJson}; const provinceSelect = document.querySelector('select[name*="DID Province"]'); const cityInput = document.querySelector('input[name*="DID City"]'); if (cityInput && cities.length > 0) { // Create datalist for city suggestions const datalist = document.createElement('datalist'); datalist.id = 'citySuggestions'; cities.forEach(function(city) { const option = document.createElement('option'); option.value = city; datalist.appendChild(option); }); document.body.appendChild(datalist); cityInput.setAttribute('list', 'citySuggestions'); cityInput.setAttribute('autocomplete', 'off'); } // Update city placeholder based on province selection if (provinceSelect && cityInput) { provinceSelect.addEventListener('change', function() { const province = this.value; cityInput.placeholder = 'Type city name for ' + province; }); // Trigger initial placeholder if (provinceSelect.value) { cityInput.placeholder = 'Type city name for ' + provinceSelect.value; } } }); HTML; } }); /** * Hook to validate DID selection before order */ add_hook('ShoppingCartValidateProduct', 1, function($vars) { $configOptions = $vars['configoptions']; // Check if DID province/city are selected but no specific DID if (!empty($configOptions['DID Province']) && empty($configOptions['DID Number'])) { $province = $configOptions['DID Province']; $city = $configOptions['DID City'] ?? ''; try { $query = Capsule::table('mod_fusionpbx_dids') ->where('status', 'available') ->where('province', $province); if (!empty($city)) { $query->where('city', 'like', '%' . $city . '%'); } $availableDIDs = $query->count(); if ($availableDIDs === 0) { return "No available DIDs found for {$province}" . (!empty($city) ? " - {$city}" : "") . ". Please select a different province/city or contact support."; } } catch (Exception $e) { // If database error, allow order to proceed logActivity("FusionPBX DID Validation Error: " . $e->getMessage()); } } return null; // No error }); ?>