diff --git a/.claude/settings.local.json b/.claude/settings.local.json index e5e75dbf..eb532d69 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -2,46 +2,24 @@ "$schema": "https://json.schemastore.org/claude-code-settings.json", "permissions": { "allow": [ - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html && wp post get 5346 --fields=ID,post_title,post_name\")", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"tail -30 /home/974670.cloudwaysapps.com/uberrxmprk/public_html/wp-content/debug.log | grep -i ''hvac announcements admin''\")", + "Bash(SSHPASS=:* sshpass -e ssh :*)", "mcp__playwright__browser_navigate", "mcp__playwright__browser_evaluate", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"tail -200 /home/974670.cloudwaysapps.com/uberrxmprk/public_html/wp-content/debug.log | grep -i ''enqueue''\")", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"tail -300 /home/974670.cloudwaysapps.com/uberrxmprk/public_html/wp-content/debug.log | grep -i -E ''(wp_enqueue_scripts|init_hooks)''\")", "Bash(scripts/deploy.sh:*)", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"tail -100 /home/974670.cloudwaysapps.com/uberrxmprk/public_html/wp-content/debug.log | grep -i ''HVAC_Announcements_Admin''\")", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"tail -150 /home/974670.cloudwaysapps.com/uberrxmprk/public_html/wp-content/debug.log | grep -i ''HVAC_Announcements_Admin''\")", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"tail -200 /home/974670.cloudwaysapps.com/uberrxmprk/public_html/wp-content/debug.log\")", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html && wp cache flush\")", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"tail -50 /home/974670.cloudwaysapps.com/uberrxmprk/public_html/wp-content/debug.log | grep -i ''HVAC''\")", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"tail -200 /home/974670.cloudwaysapps.com/uberrxmprk/public_html/wp-content/debug.log | grep -A5 ''HVAC_Announcements_Admin.*enqueue_admin_assets called''\")", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"tail -500 /home/974670.cloudwaysapps.com/uberrxmprk/public_html/wp-content/debug.log | grep -i ''enqueue.*admin.*assets\\|ENQUEUING SCRIPTS''\")", - "mcp__zen__debug", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html && wp user get 25 --field=roles\")", "WebSearch", + "mcp__zen__debug", "mcp__zen__chat", "mcp__playwright__browser_click", "mcp__playwright__browser_take_screenshot", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"grep -A5 ''wp_enqueue_script.*hvac-announcements-admin'' /home/974670.cloudwaysapps.com/uberrxmprk/public_html/wp-content/plugins/hvac-community-events/includes/class-hvac-announcements-admin.php | head -20\")", "mcp__zen__analyze", "mcp__playwright__browser_type", "mcp__playwright__browser_close", "mcp__playwright__browser_install", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html && wp option get hvac_zoho_client_id 2>/dev/null || echo ''NOT SET''\")", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html && wp option get hvac_zoho_refresh_token 2>/dev/null || echo ''NOT SET''\")", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html && wp eval ''\nrequire_once ABSPATH . \"\"wp-content/plugins/hvac-community-events/includes/zoho/class-zoho-crm-auth.php\"\";\n$auth = new HVAC_Zoho_CRM_Auth();\n\necho \"\"=== ZOHO CRM READ-ONLY TEST ===\"\";\necho \"\"\\n\\n1. ORGANIZATION INFO:\\n\"\";\n$org = $auth->make_api_request(\"\"/org\"\", \"\"GET\"\");\nif (isset($org[\"\"org\"\"][0])) {\n $o = $org[\"\"org\"\"][0];\n echo \"\" Company: \"\" . $o[\"\"company_name\"\"] . \"\"\\n\"\";\n echo \"\" Country: \"\" . ($o[\"\"country\"\"] ?? \"\"N/A\"\") . \"\"\\n\"\";\n echo \"\" Time Zone: \"\" . ($o[\"\"time_zone\"\"] ?? \"\"N/A\"\") . \"\"\\n\"\";\n} else {\n echo \"\" Error: \"\" . print_r($org, true);\n}\n''\")", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html && wp eval ''\nrequire_once ABSPATH . \"\"wp-content/plugins/hvac-community-events/includes/zoho/class-zoho-crm-auth.php\"\";\n$auth = new HVAC_Zoho_CRM_Auth();\n\necho \"\"2. CONTACTS (first 5):\\n\"\";\n$contacts = $auth->make_api_request(\"\"/Contacts?per_page=5\"\", \"\"GET\"\");\nif (isset($contacts[\"\"data\"\"])) {\n foreach ($contacts[\"\"data\"\"] as $c) {\n echo \"\" - \"\" . ($c[\"\"Full_Name\"\"] ?? $c[\"\"Email\"\"] ?? \"\"Unknown\"\") . \"\"\\n\"\";\n }\n echo \"\" Total in response: \"\" . count($contacts[\"\"data\"\"]) . \"\"\\n\"\";\n} elseif (isset($contacts[\"\"info\"\"])) {\n echo \"\" No contacts found (empty CRM)\\n\"\";\n} else {\n echo \"\" Response: \"\" . print_r($contacts, true);\n}\n''\")", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html && wp eval ''\nrequire_once ABSPATH . \"\"wp-content/plugins/hvac-community-events/includes/zoho/class-zoho-crm-auth.php\"\";\n$auth = new HVAC_Zoho_CRM_Auth();\n\necho \"\"3. CAMPAIGNS (first 5):\\n\"\";\n$campaigns = $auth->make_api_request(\"\"/Campaigns?per_page=5\"\", \"\"GET\"\");\nif (isset($campaigns[\"\"data\"\"])) {\n foreach ($campaigns[\"\"data\"\"] as $c) {\n echo \"\" - \"\" . ($c[\"\"Campaign_Name\"\"] ?? \"\"Unnamed\"\") . \"\"\\n\"\";\n }\n} elseif (isset($campaigns[\"\"code\"\"]) && $campaigns[\"\"code\"\"] == \"\"INVALID_MODULE\"\") {\n echo \"\" Campaigns module not enabled\\n\"\";\n} else {\n echo \"\" Response: \"\" . json_encode($campaigns) . \"\"\\n\"\";\n}\n\necho \"\"\\n4. USERS (CRM users):\\n\"\";\n$users = $auth->make_api_request(\"\"/users?type=AllUsers\"\", \"\"GET\"\");\nif (isset($users[\"\"users\"\"])) {\n foreach (array_slice($users[\"\"users\"\"], 0, 5) as $u) {\n echo \"\" - \"\" . $u[\"\"full_name\"\"] . \"\" (\"\" . $u[\"\"role\"\"][\"\"name\"\"] . \"\")\\n\"\";\n }\n} else {\n echo \"\" Response: \"\" . json_encode($users) . \"\"\\n\"\";\n}\n''\")", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html && wp eval ''\necho \"\"=== EVENT TICKETS DATA STRUCTURE ===\"\";\necho \"\"\\n\"\";\n\n// Check what ticket-related post types exist\n$post_types = get_post_types(array(), \"\"names\"\");\n$ticket_types = array_filter($post_types, function($pt) {\n return strpos($pt, \"\"ticket\"\") !== false || strpos($pt, \"\"tec\"\") !== false || strpos($pt, \"\"tribe\"\") !== false || strpos($pt, \"\"attendee\"\") !== false || strpos($pt, \"\"order\"\") !== false;\n});\necho \"\"Ticket-related post types:\\n\"\";\nforeach ($ticket_types as $pt) {\n $count = wp_count_posts($pt);\n $total = isset($count->publish) ? $count->publish : 0;\n echo \"\" - \"\" . $pt . \"\" (\"\" . $total . \"\" published)\\n\"\";\n}\n\necho \"\"\\n\"\";\n\n// Check for Tickets Commerce orders\nif (class_exists(\"\"TEC\\Tickets\\Commerce\\Order\"\")) {\n echo \"\"Tickets Commerce Order class exists\\n\"\";\n}\n\n// Check for attendees\n$attendees = get_posts(array(\n \"\"post_type\"\" => \"\"tribe_tpp_attendees\"\",\n \"\"posts_per_page\"\" => 5,\n \"\"post_status\"\" => \"\"any\"\"\n));\necho \"\"\\nAttendees (tribe_tpp_attendees): \"\" . count($attendees) . \"\"\\n\"\";\n\n// Try tribe_rsvp_attendees\n$rsvp = get_posts(array(\n \"\"post_type\"\" => \"\"tribe_rsvp_attendees\"\", \n \"\"posts_per_page\"\" => 5,\n \"\"post_status\"\" => \"\"any\"\"\n));\necho \"\"RSVPs (tribe_rsvp_attendees): \"\" . count($rsvp) . \"\"\\n\"\";\n\n// Try tec_tc_attendee (Tickets Commerce)\n$tc_attendees = get_posts(array(\n \"\"post_type\"\" => \"\"tec_tc_attendee\"\",\n \"\"posts_per_page\"\" => 5, \n \"\"post_status\"\" => \"\"any\"\"\n));\necho \"\"TC Attendees (tec_tc_attendee): \"\" . count($tc_attendees) . \"\"\\n\"\";\n\n// Try tec_tc_order\n$tc_orders = get_posts(array(\n \"\"post_type\"\" => \"\"tec_tc_order\"\",\n \"\"posts_per_page\"\" => 5,\n \"\"post_status\"\" => \"\"any\"\"\n));\necho \"\"TC Orders (tec_tc_order): \"\" . count($tc_orders) . \"\"\\n\"\";\n''\")", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 \"cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html && wp eval ''\necho \"\"=== EVENT TICKETS DATA STRUCTURE ===\\n\\n\"\";\n\n// Check ticket-related post types\n$post_types = get_post_types(array(), \"\"names\"\");\necho \"\"Ticket-related post types:\\n\"\";\nforeach ($post_types as $pt) {\n if (strpos($pt, \"\"ticket\"\") !== false || strpos($pt, \"\"attendee\"\") !== false || strpos($pt, \"\"rsvp\"\") !== false || strpos($pt, \"\"tec_tc\"\") !== false) {\n $count = wp_count_posts($pt);\n $total = isset($count->publish) ? $count->publish : 0;\n echo \"\" - \"\" . $pt . \"\" (\"\" . $total . \"\" published)\\n\"\";\n }\n}\n''\")", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 'cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html && cat > /tmp/check_orders.php << ''''PHPCODE''''\n \"\"tec_tc_order\"\",\n \"\"posts_per_page\"\" => 3,\n \"\"post_status\"\" => \"\"any\"\"\n));\n\necho \"\"Found \"\" . count($orders) . \"\" sample orders\\n\\n\"\";\n\nforeach ($orders as $order) {\n echo \"\"--- Order #\"\" . $order->ID . \"\" ---\\n\"\";\n echo \"\"Status: \"\" . $order->post_status . \"\"\\n\"\";\n echo \"\"Date: \"\" . $order->post_date . \"\"\\n\"\";\n \n $meta = get_post_meta($order->ID);\n \n // Key fields\n $fields = array(\n \"\"_tec_tc_order_purchaser_name\"\",\n \"\"_tec_tc_order_purchaser_email\"\", \n \"\"_tec_tc_order_total\"\",\n \"\"_tec_tc_order_gateway\"\",\n \"\"_tec_tc_order_gateway_order_id\"\",\n \"\"_tec_tc_order_items\"\"\n );\n \n foreach ($fields as $f) {\n if (isset($meta[$f])) {\n $val = $meta[$f][0];\n if (is_serialized($val)) {\n $val = \"\"SERIALIZED: \"\" . substr($val, 0, 100) . \"\"...\"\";\n }\n echo \"\"$f: $val\\n\"\";\n }\n }\n echo \"\"\\n\"\";\n}\n\n// Also check attendees linked to orders\necho \"\"=== SAMPLE ATTENDEE WITH ORDER LINK ===\\n\"\";\n$attendee = get_posts(array(\n \"\"post_type\"\" => \"\"tec_tc_attendee\"\",\n \"\"posts_per_page\"\" => 1,\n \"\"post_status\"\" => \"\"any\"\"\n));\nif ($attendee) {\n $a = $attendee[0];\n $meta = get_post_meta($a->ID);\n echo \"\"Attendee ID: \"\" . $a->ID . \"\"\\n\"\";\n echo \"\"Event ID: \"\" . ($meta[\"\"_tec_tc_attendee_event\"\"][0] ?? \"\"N/A\"\") . \"\"\\n\"\";\n echo \"\"Order ID: \"\" . ($meta[\"\"_tec_tc_order\"\"][0] ?? $meta[\"\"_tribe_tpp_order\"\"][0] ?? \"\"N/A\"\") . \"\"\\n\"\";\n echo \"\"Ticket ID: \"\" . ($meta[\"\"_tec_tc_attendee_ticket\"\"][0] ?? \"\"N/A\"\") . \"\"\\n\"\";\n echo \"\"Name: \"\" . ($meta[\"\"_tec_tc_attendee_name\"\"][0] ?? $meta[\"\"_tribe_tickets_full_name\"\"][0] ?? \"\"N/A\"\") . \"\"\\n\"\";\n echo \"\"Email: \"\" . ($meta[\"\"_tec_tc_attendee_email\"\"][0] ?? $meta[\"\"_tribe_tickets_email\"\"][0] ?? \"\"N/A\"\") . \"\"\\n\"\";\n}\nPHPCODE\nphp /tmp/check_orders.php')", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 'cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html && cat > /tmp/test_sync.php << ''''PHPCODE''''\nsync_events();\necho \"\" Total: \"\" . $result[\"\"total\"\"] . \"\", Would Sync: \"\" . $result[\"\"synced\"\"] . \"\"\\n\"\";\nif (!empty($result[\"\"test_data\"\"])) {\n $sample = $result[\"\"test_data\"\"][0];\n echo \"\" Sample: \"\" . substr($sample[\"\"event_title\"\"], 0, 40) . \"\"...\\n\"\";\n}\necho \"\"\\n\"\";\n\n// 2. Users (Trainers)\necho \"\"2. TRAINERS → CONTACTS\\n\"\";\n$result = $sync->sync_users();\necho \"\" Total: \"\" . $result[\"\"total\"\"] . \"\", Would Sync: \"\" . $result[\"\"synced\"\"] . \"\"\\n\"\";\nif (!empty($result[\"\"test_data\"\"])) {\n $sample = $result[\"\"test_data\"\"][0];\n echo \"\" Sample: \"\" . $sample[\"\"user_email\"\"] . \"\" (\"\" . $sample[\"\"user_role\"\"] . \"\")\\n\"\";\n}\necho \"\"\\n\"\";\n\n// 3. Attendees\necho \"\"3. ATTENDEES → CONTACTS + CAMPAIGN MEMBERS\\n\"\";\n$result = $sync->sync_attendees();\necho \"\" Total: \"\" . $result[\"\"total\"\"] . \"\", Would Sync: \"\" . $result[\"\"synced\"\"] . \"\"\\n\"\";\nif (!empty($result[\"\"test_data\"\"])) {\n $sample = $result[\"\"test_data\"\"][0];\n echo \"\" Sample: \"\" . ($sample[\"\"full_name\"\"] ?: \"\"Unknown\"\") . \"\" <\"\" . ($sample[\"\"email\"\"] ?: \"\"no-email\"\") . \"\">\\n\"\";\n echo \"\" Event: \"\" . ($sample[\"\"event_title\"\"] ?: \"\"N/A\"\") . \"\"\\n\"\";\n}\necho \"\"\\n\"\";\n\n// 4. RSVPs\necho \"\"4. RSVPs → LEADS + CAMPAIGN MEMBERS\\n\"\";\n$result = $sync->sync_rsvps();\necho \"\" Total: \"\" . $result[\"\"total\"\"] . \"\", Would Sync: \"\" . $result[\"\"synced\"\"] . \"\"\\n\"\";\nif (!empty($result[\"\"test_data\"\"])) {\n $sample = $result[\"\"test_data\"\"][0];\n echo \"\" Sample: \"\" . ($sample[\"\"full_name\"\"] ?: \"\"Unknown\"\") . \"\" <\"\" . ($sample[\"\"email\"\"] ?: \"\"no-email\"\") . \"\">\\n\"\";\n echo \"\" RSVP Status: \"\" . ($sample[\"\"rsvp_status\"\"] ?: \"\"N/A\"\") . \"\"\\n\"\";\n}\necho \"\"\\n\"\";\n\n// 5. Purchases/Orders\necho \"\"5. TICKET ORDERS → INVOICES\\n\"\";\n$result = $sync->sync_purchases();\necho \"\" Total: \"\" . $result[\"\"total\"\"] . \"\", Would Sync: \"\" . $result[\"\"synced\"\"] . \"\"\\n\"\";\nif (!empty($result[\"\"test_data\"\"])) {\n $sample = $result[\"\"test_data\"\"][0];\n echo \"\" Sample: Order #\"\" . $sample[\"\"order_id\"\"] . \"\" - \"\" . $sample[\"\"purchaser_email\"\"] . \"\"\\n\"\";\n echo \"\" Gateway: \"\" . $sample[\"\"gateway\"\"] . \"\", Date: \"\" . $sample[\"\"date\"\"] . \"\"\\n\"\";\n}\necho \"\"\\n\"\";\n\necho \"\"=== ALL SYNCS IN STAGING MODE (NO DATA SENT TO ZOHO) ===\\n\"\";\nPHPCODE\nphp /tmp/test_sync.php')", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 'tail -50 /home/974670.cloudwaysapps.com/uberrxmprk/public_html/wp-content/debug.log 2>/dev/null | grep -i \"\"zoho\\|fatal\\|error\"\" | tail -20')", "Bash(./scripts/deploy.sh:*)", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 'ls -la /home/974670.cloudwaysapps.com/uberrxmprk/public_html/wp-content/plugins/hvac-community-events/assets/css/zoho-admin.css 2>/dev/null || echo \"\"File does not exist on staging\"\"')", - "Bash(SSHPASS=\"uSCO6f1y\" sshpass -e ssh -o StrictHostKeyChecking=no roodev@146.190.76.204 'curl -s \"\"https://upskill-staging.measurequick.com/master-trainer/master-dashboard/\"\" 2>/dev/null | sed -n \"\"220,235p\"\"')", "Bash(curl:*)", - "Bash(yes:*)" + "Bash(yes:*)", + "WebFetch(domain:json.schemastore.org)", + "WebFetch(domain:www.schemastore.org)" ], "deny": [], "ask": [], diff --git a/Status.md b/Status.md index b60f8e49..51104f9a 100644 --- a/Status.md +++ b/Status.md @@ -1,7 +1,7 @@ # HVAC Community Events - Project Status -**Last Updated:** December 20, 2025 -**Current Session:** Scheduled Sync Persistence Fix - Complete +**Last Updated:** December 21, 2025 +**Current Session:** Public Map Fix (Strategy H) - In Verification **Version:** 2.1.11 (Deployed to Production) --- @@ -39,26 +39,30 @@ --- -## 🎯 CURRENT SESSION - PUBLIC MAP & DIRECTORY FIX (Dec 20, 2025) +## 🎯 CURRENT SESSION - PUBLIC MAP & DIRECTORY FIX (Dec 21, 2025) -### Status: 🚧 **IN PROGRESS (Debugging)** +### Status: 🔄 **IN PROGRESS - Deployed to Staging (Strategy H)** -**Problem:** "Find a Trainer" map loads briefly then disappears, replaced by "Map Temporarily Unavailable". Directory filters working but map unstable. +**Problem:** Map markers missing. Data analysis reveals markers have identical Latitude and Longitude values (corruption). -**Findings:** -1. **Safari Blocker Bug:** Identified and fixed `HVAC_Find_Trainer_Assets` correctly blocking assets on Safari. -2. **Safety Script Race Condition:** `mapgeo-safety.js` has a hard 6-second timeout loop that conflicts with slower resource loading. -3. **Stale Cache Issue:** Browser continues to serve old `mapgeo-safety.js` (6s timeout) despite file update (30s timeout) and version bump (`.fix1`). -4. **Environment Constraints:** `wp cache flush` failed (command not found), indicating potential restriction on direct WP-CLI use. +**Root Cause:** +- IGM Plugin's client-side processing corrupts `longitude` by overwriting it with `latitude` immediately before rendering. +- Previous PHP Injection strategies (D-G) caused 500 errors due to `WP_Query` timing/context issues in the footer. -**Fixes Applied (Pending Verification):** -1. ✅ **Removed Safari Blocker:** Corrected logic in `class-hvac-find-trainer-assets.php`. -2. ✅ **Increased Timeouts:** Updated `assets/js/mapgeo-safety.js` to 30s global timeout. -3. ✅ **Version Bump:** Added `.fix1` suffix to enqueue version in `class-hvac-mapgeo-safety.php`. +**Solution (Strategy H):** +1. ✅ **Javascript Interceptor:** Injected a robust interceptor script in `class-hvac-mapgeo-integration.php`. +2. ✅ **Mechanism:** Uses `Object.defineProperty` on `window.iMapsData` to catch the data assignment *before* the map plugin sees it. +3. ✅ **Repair Logic:** Detects corrupted markers (Lat == Lng) and instantly restores correct values from safe `lat`/`lng` backup keys provided by PHP. +4. ✅ **Cleanup:** Removed dangerous PHP query injections that were causing server errors. + +**Current Status:** +- Fix deployed to Staging. +- Pending verification of visible markers and "Healed" console logs. **Next Steps:** -- Resolve caching issue to ensure updated `mapgeo-safety.js` is served. -- Verify map stability with 30s timeout. +1. Verify map renders correctly on Staging. +2. Deploy to Production. + --- diff --git a/assets/js/find-trainer.js b/assets/js/find-trainer.js index 97dfe4c6..b6e4b105 100644 --- a/assets/js/find-trainer.js +++ b/assets/js/find-trainer.js @@ -6,7 +6,7 @@ * @since 1.0.0 */ -(function($) { +(function ($) { 'use strict'; // Cache DOM elements @@ -17,24 +17,42 @@ let isLoading = false; // Initialize on document ready - $(document).ready(function() { + $(document).ready(function () { + initTrainerDirectory(); + }); + + /** + * Initialize trainer directory with retries + */ + function initTrainerDirectory(attempts = 0) { + // Critical dependency check + if (typeof hvac_find_trainer === 'undefined') { + if (attempts < 10) { + console.log(`[HVAC Find Trainer] Configuration object missing, retrying... (${attempts + 1}/10)`); + setTimeout(() => initTrainerDirectory(attempts + 1), 500); + return; + } + console.error('[HVAC Find Trainer] Failed to initialize: hvac_find_trainer object missing after retries'); + return; + } + initializeElements(); bindEvents(); - + // Handle direct profile URL access handleDirectProfileAccess(); - + // Enable MapGeo interaction handling (only if not showing direct profile) if (!hvac_find_trainer.show_direct_profile) { preventMapGeoSidebarContent(); interceptMapGeoMarkers(); - + // Additional MapGeo integration after map loads - setTimeout(function() { + setTimeout(function () { initializeMapGeoEvents(); }, 2000); // Give MapGeo time to initialize } - }); + } /** * Initialize cached elements @@ -43,7 +61,7 @@ $filterModal = $('#hvac-filter-modal'); $trainerModal = $('#hvac-trainer-modal'); $contactForm = $('#hvac-contact-form'); - + // CRITICAL: Ensure modals are hidden on initialization if ($filterModal.length) { $filterModal.removeClass('modal-active active show').css({ @@ -65,15 +83,15 @@ // Remove any MapGeo sidebar content immediately $('.igm_content_right_1_3').remove(); $('.igm_content_gutter').remove(); - + // Watch for any dynamic content injection from MapGeo - const observer = new MutationObserver(function(mutations) { - mutations.forEach(function(mutation) { + const observer = new MutationObserver(function (mutations) { + mutations.forEach(function (mutation) { if (mutation.addedNodes.length) { - mutation.addedNodes.forEach(function(node) { + mutation.addedNodes.forEach(function (node) { if (node.nodeType === 1) { // Element node // Remove any MapGeo sidebar that gets added - if ($(node).hasClass('igm_content_right_1_3') || + if ($(node).hasClass('igm_content_right_1_3') || $(node).hasClass('igm_content_gutter')) { $(node).remove(); } @@ -84,7 +102,7 @@ } }); }); - + // Observe the map section for changes const mapSection = document.querySelector('.hvac-map-section'); if (mapSection) { @@ -93,25 +111,25 @@ subtree: true }); } - + // Also observe the entire page for MapGeo injections observer.observe(document.body, { childList: true, subtree: true }); } - + /** * Initialize MapGeo-specific event handlers after map loads */ function initializeMapGeoEvents() { console.log('Initializing MapGeo events...'); - + // Create the main MapGeo handler function // This replaces the early version created in the page template - window.hvacMainShowTrainerModal = function(data) { + window.hvacMainShowTrainerModal = function (data) { console.log('MapGeo custom action triggered with data:', data); - + // Method 1: Use profile_id if available (most reliable) let profileId = null; if (data && data.profile_id && data.profile_id.trim() !== '') { @@ -126,16 +144,16 @@ // Check if id field contains just the profile ID number profileId = data.id; } - + console.log('Extracted profile ID:', profileId); - + if (profileId) { // Find trainer card by profile ID (most reliable method) const $matchingCard = $('.hvac-trainer-card[data-profile-id="' + profileId + '"]'); - + if ($matchingCard.length > 0 && !$matchingCard.hasClass('hvac-champion-card')) { console.log('Found matching trainer card by profile ID:', profileId); - + // Extract trainer data from the card const trainerData = { profile_id: profileId, @@ -151,9 +169,9 @@ training_locations: 'On-site, Remote', upcoming_events: [] }; - + // Extract certifications from card badges - $matchingCard.find('.hvac-trainer-cert-badge').each(function() { + $matchingCard.find('.hvac-trainer-cert-badge').each(function () { const certText = $(this).text().trim(); if (certText && certText !== 'HVAC Trainer') { trainerData.certifications.push({ @@ -162,7 +180,7 @@ }); } }); - + // Show the trainer modal showTrainerModal(trainerData); return; // Successfully handled @@ -173,17 +191,17 @@ console.warn('No trainer card found for profile ID:', profileId); } } - + // Fallback Method 2: Try to extract trainer name and match let trainerName = null; - + // Try various name fields if (data && data.name && data.name.trim() !== '+' && !data.name.match(/^\d+$/)) { trainerName = data.name.trim(); } else if (data && data.title && data.title.trim() !== '+') { trainerName = data.title.trim(); } - + // Try content field if (!trainerName && data && data.content) { console.log('Trying to extract trainer from content:', data.content); @@ -194,7 +212,7 @@ trainerName = nameElements[0].textContent.trim(); } } - + // Try tooltipContent if (!trainerName && data && data.tooltipContent) { const tempDiv = document.createElement('div'); @@ -204,28 +222,28 @@ trainerName = strongElements[0].textContent.trim(); } } - + console.log('Extracted trainer name (fallback):', trainerName); - + if (trainerName && trainerName !== '+') { // Try to find matching trainer by name - let $matchingCard = $('.hvac-trainer-card').filter(function() { + let $matchingCard = $('.hvac-trainer-card').filter(function () { const cardName = $(this).find('.hvac-trainer-name a, .hvac-trainer-name .hvac-champion-name').text().trim(); return cardName === trainerName; }); - + // If exact match not found, try partial matching if ($matchingCard.length === 0) { - $matchingCard = $('.hvac-trainer-card').filter(function() { + $matchingCard = $('.hvac-trainer-card').filter(function () { const cardName = $(this).find('.hvac-trainer-name a, .hvac-trainer-name .hvac-champion-name').text().trim(); - return cardName.toLowerCase().includes(trainerName.toLowerCase()) || - trainerName.toLowerCase().includes(cardName.toLowerCase()); + return cardName.toLowerCase().includes(trainerName.toLowerCase()) || + trainerName.toLowerCase().includes(cardName.toLowerCase()); }); } - + if ($matchingCard.length > 0 && !$matchingCard.hasClass('hvac-champion-card')) { console.log('Found matching trainer card by name:', trainerName); - + // Extract trainer data from the card const trainerData = { profile_id: $matchingCard.data('profile-id'), @@ -241,9 +259,9 @@ training_locations: 'On-site, Remote', upcoming_events: [] }; - + // Extract certifications from card badges - $matchingCard.find('.hvac-trainer-cert-badge').each(function() { + $matchingCard.find('.hvac-trainer-cert-badge').each(function () { const certText = $(this).text().trim(); if (certText && certText !== 'HVAC Trainer') { trainerData.certifications.push({ @@ -252,15 +270,15 @@ }); } }); - + // Show the trainer modal showTrainerModal(trainerData); } else if ($matchingCard.length > 0 && $matchingCard.hasClass('hvac-champion-card')) { console.log('Matched trainer is a Champion, not showing modal'); } else { console.warn('No matching trainer found for name:', trainerName); - console.log('Available trainers:', - $('.hvac-trainer-card .hvac-trainer-name a, .hvac-trainer-card .hvac-trainer-name .hvac-champion-name').map(function() { + console.log('Available trainers:', + $('.hvac-trainer-card .hvac-trainer-name a, .hvac-trainer-card .hvac-trainer-name .hvac-champion-name').map(function () { return $(this).text().trim(); }).get() ); @@ -268,46 +286,46 @@ } else { console.warn('Could not extract valid trainer identifier from MapGeo data:', data); console.log('Available data properties:', Object.keys(data || {})); - console.log('Available profile IDs on page:', - $('.hvac-trainer-card').map(function() { + console.log('Available profile IDs on page:', + $('.hvac-trainer-card').map(function () { return $(this).data('profile-id'); }).get() ); } }; - + // Replace the early function with the main one window.hvacShowTrainerModal = window.hvacMainShowTrainerModal; - + // Process any queued calls from before the main script loaded if (window.hvacPendingModalCalls && window.hvacPendingModalCalls.length > 0) { console.log('Processing', window.hvacPendingModalCalls.length, 'queued MapGeo calls'); - window.hvacPendingModalCalls.forEach(function(data) { + window.hvacPendingModalCalls.forEach(function (data) { window.hvacMainShowTrainerModal(data); }); window.hvacPendingModalCalls = []; // Clear the queue } - + console.log('MapGeo custom action function created: window.hvacShowTrainerModal'); } - - + + /** * Prevent MapGeo from showing content in sidebar (if needed) */ function interceptMapGeoMarkers() { // This function now primarily handles preventing MapGeo sidebar content // The actual marker clicks are handled via the MapGeo custom action: window.hvacShowTrainerModal - + // Handle any legacy view profile links if they exist in tooltips/popups - $(document).on('click', '.hvac-view-profile, .hvac-marker-popup button', function(e) { + $(document).on('click', '.hvac-view-profile, .hvac-marker-popup button', function (e) { e.preventDefault(); e.stopPropagation(); const profileId = $(this).data('profile-id'); if (profileId) { // Find the corresponding trainer data from the cards const $trainerCard = $('.hvac-trainer-card[data-profile-id="' + profileId + '"]'); - + if ($trainerCard.length > 0 && !$trainerCard.hasClass('hvac-champion-card')) { // Get trainer name and trigger the MapGeo custom action const trainerName = $trainerCard.find('.hvac-trainer-name a, .hvac-trainer-name .hvac-champion-name').text().trim(); @@ -326,49 +344,49 @@ function bindEvents() { // Filter button clicks - handle both class variations $('.hvac-filter-btn, .hvac-filter-button').on('click', handleFilterClick); - + // Filter modal apply $('.hvac-filter-apply').on('click', applyFilters); - + // Clear all filters button $('.hvac-clear-filters').on('click', clearAllFilters); - + // Trainer profile clicks - using event delegation $(document).on('click', '.hvac-open-profile', handleProfileClick); - + // Modal close buttons and backdrop clicks $('.hvac-modal-close').on('click', closeModals); - + // Click on modal backdrop to close - $filterModal.on('click', function(e) { + $filterModal.on('click', function (e) { if ($(e.target).is('#hvac-filter-modal')) { closeModals(); } }); - - $trainerModal.on('click', function(e) { + + $trainerModal.on('click', function (e) { if ($(e.target).is('#hvac-trainer-modal')) { closeModals(); } }); - + // Escape key to close modals - $(document).on('keydown', function(e) { + $(document).on('keydown', function (e) { if (e.key === 'Escape') { closeModals(); } }); - + // Search input $('.hvac-search-input').on('input', debounce(handleSearch, 500)); - + // Contact form submission (both modal and direct forms) $contactForm.on('submit', handleContactSubmit); $(document).on('submit', '#hvac-direct-contact-form', handleContactSubmit); - + // Pagination clicks $(document).on('click', '.hvac-pagination a, .hvac-page-link', handlePagination); - + // Active filter removal $(document).on('click', '.hvac-active-filter button', removeActiveFilter); } @@ -380,7 +398,7 @@ e.preventDefault(); e.stopPropagation(); currentFilter = $(this).data('filter'); - + // Load real filter options via AJAX loadFilterOptions(currentFilter); } @@ -390,51 +408,51 @@ */ function loadFilterOptions(filterType) { if (isLoading) return; - + isLoading = true; - + // Show loading state for filter button $(`.hvac-filter-btn[data-filter="${filterType}"]`).addClass('loading'); - + $.post(hvac_find_trainer.ajax_url, { action: 'hvac_get_filter_options', filter_type: filterType, nonce: hvac_find_trainer.nonce }) - .done(function(response) { - if (response.success && response.data.options) { - // Convert the different response formats to standard format - let options = []; - - if (filterType === 'business_type') { - // Business types have {value, label, count} format - options = response.data.options; + .done(function (response) { + if (response.success && response.data.options) { + // Convert the different response formats to standard format + let options = []; + + if (filterType === 'business_type') { + // Business types have {value, label, count} format + options = response.data.options; + } else { + // States and other simple arrays need to be converted to {value, label} format + options = response.data.options.map(function (option) { + if (typeof option === 'string') { + return { value: option, label: option }; + } + return option; + }); + } + + showFilterModal({ options: options }); } else { - // States and other simple arrays need to be converted to {value, label} format - options = response.data.options.map(function(option) { - if (typeof option === 'string') { - return {value: option, label: option}; - } - return option; - }); + console.error('Failed to load filter options:', response); + // Fallback to empty options + showFilterModal({ options: [] }); } - - showFilterModal({options: options}); - } else { - console.error('Failed to load filter options:', response); + }) + .fail(function (xhr, status, error) { + console.error('AJAX error loading filter options:', status, error); // Fallback to empty options - showFilterModal({options: []}); - } - }) - .fail(function(xhr, status, error) { - console.error('AJAX error loading filter options:', status, error); - // Fallback to empty options - showFilterModal({options: []}); - }) - .always(function() { - isLoading = false; - $(`.hvac-filter-btn[data-filter="${filterType}"]`).removeClass('loading'); - }); + showFilterModal({ options: [] }); + }) + .always(function () { + isLoading = false; + $(`.hvac-filter-btn[data-filter="${filterType}"]`).removeClass('loading'); + }); } /** @@ -443,41 +461,41 @@ function getMockFilterOptions(filterType) { const options = { state: [ - {value: 'Alabama', label: 'Alabama'}, - {value: 'Alaska', label: 'Alaska'}, - {value: 'Arizona', label: 'Arizona'}, - {value: 'Arkansas', label: 'Arkansas'}, - {value: 'California', label: 'California'}, - {value: 'Colorado', label: 'Colorado'}, - {value: 'Florida', label: 'Florida'}, - {value: 'Georgia', label: 'Georgia'}, - {value: 'Illinois', label: 'Illinois'}, - {value: 'Michigan', label: 'Michigan'}, - {value: 'Minnesota', label: 'Minnesota'}, - {value: 'Ohio', label: 'Ohio'}, - {value: 'Texas', label: 'Texas'}, - {value: 'Wisconsin', label: 'Wisconsin'} + { value: 'Alabama', label: 'Alabama' }, + { value: 'Alaska', label: 'Alaska' }, + { value: 'Arizona', label: 'Arizona' }, + { value: 'Arkansas', label: 'Arkansas' }, + { value: 'California', label: 'California' }, + { value: 'Colorado', label: 'Colorado' }, + { value: 'Florida', label: 'Florida' }, + { value: 'Georgia', label: 'Georgia' }, + { value: 'Illinois', label: 'Illinois' }, + { value: 'Michigan', label: 'Michigan' }, + { value: 'Minnesota', label: 'Minnesota' }, + { value: 'Ohio', label: 'Ohio' }, + { value: 'Texas', label: 'Texas' }, + { value: 'Wisconsin', label: 'Wisconsin' } ], business_type: [ - {value: 'Independent Contractor', label: 'Independent Contractor'}, - {value: 'Small Business', label: 'Small Business'}, - {value: 'Corporation', label: 'Corporation'}, - {value: 'Non-Profit', label: 'Non-Profit'} + { value: 'Independent Contractor', label: 'Independent Contractor' }, + { value: 'Small Business', label: 'Small Business' }, + { value: 'Corporation', label: 'Corporation' }, + { value: 'Non-Profit', label: 'Non-Profit' } ], training_format: [ - {value: 'In-Person', label: 'In-Person'}, - {value: 'Virtual', label: 'Virtual'}, - {value: 'Hybrid', label: 'Hybrid'}, - {value: 'Self-Paced', label: 'Self-Paced'} + { value: 'In-Person', label: 'In-Person' }, + { value: 'Virtual', label: 'Virtual' }, + { value: 'Hybrid', label: 'Hybrid' }, + { value: 'Self-Paced', label: 'Self-Paced' } ], training_resources: [ - {value: 'Video Tutorials', label: 'Video Tutorials'}, - {value: 'Written Guides', label: 'Written Guides'}, - {value: 'Hands-On Training', label: 'Hands-On Training'}, - {value: 'Certification Programs', label: 'Certification Programs'} + { value: 'Video Tutorials', label: 'Video Tutorials' }, + { value: 'Written Guides', label: 'Written Guides' }, + { value: 'Hands-On Training', label: 'Hands-On Training' }, + { value: 'Certification Programs', label: 'Certification Programs' } ] }; - + return { options: options[filterType] || [] }; @@ -489,17 +507,17 @@ function showFilterModal(data) { const $modalTitle = $filterModal.find('.hvac-filter-modal-title'); const $modalOptions = $filterModal.find('.hvac-filter-options'); - + // Set title let title = currentFilter.replace(/_/g, ' '); title = title.charAt(0).toUpperCase() + title.slice(1); $modalTitle.text(title); - + // Build options HTML let optionsHtml = ''; const currentValues = activeFilters[currentFilter] || []; - - data.options.forEach(function(option) { + + data.options.forEach(function (option) { const checked = currentValues.includes(option.value) ? 'checked' : ''; optionsHtml += `
No trainers found matching your criteria. Please try adjusting your filters.
Error loading trainers. Please try again.