# ShiftOps Troubleshooting Guide

**Last Updated:** 2026-02-25

## Common Errors

### 11. NPS Survey Not Appearing

**Error:**

```
NPS survey not showing on report page
```

**Solution:**

1. Check localStorage keys:

   - Verify `shiftops_report_unlocked` is set to `'true'`
   - Check `shiftops_nps_submitted` is not `'true'`
   - Check `shiftops_nps_dismissed` is not `'true'`

2. Verify display logic:

   ```javascript
   const isUnlocked =
     localStorage.getItem("shiftops_report_unlocked") === "true";
   const npsSubmitted =
     localStorage.getItem("shiftops_nps_submitted") === "true";
   const npsDismissed =
     localStorage.getItem("shiftops_nps_dismissed") === "true";
   const shouldShowNPS = isUnlocked && !npsSubmitted && !npsDismissed;
   ```

3. Check NPS container visibility:
   ```javascript
   const npsContainer = document.getElementById("nps-survey-container");
   if (shouldShowNPS) {
     npsContainer.style.display = "block";
     npsContainer.classList.add("show");
   }
   ```

**Debug:**

```javascript
// Check localStorage state
console.log(
  "Report unlocked:",
  localStorage.getItem("shiftops_report_unlocked")
);
console.log("NPS submitted:", localStorage.getItem("shiftops_nps_submitted"));
console.log("NPS dismissed:", localStorage.getItem("shiftops_nps_dismissed"));
```

---

### 12. NPS Submission Failures

**Error:**

```
NPS submission failed: HTTP 400
```

**Solution:**

1. Verify required fields:

   - `email`: Must be valid email format
   - `nps_score`: Must be integer 0-10

2. Check HubSpot form ID:

   - Current form ID: `804459f7-c18d-4da6-8a0b-a81f44bb8275`
   - Verify form exists in HubSpot portal `145133546`

3. Review error response:

   ```json
   {
     "success": false,
     "error": "Valid NPS score (0-10) is required",
     "http_code": 400
   }
   ```

4. Check logs:
   - Review `logs/shiftops-nps.log` for detailed error information
   - Check HubSpot API response in logs

**Debug:**

```bash
# Test NPS endpoint
curl -X POST http://localhost:8003/v2/api/shiftops-nps.php \
  -H "Content-Type: application/json" \
  -d '{
    "email": "test@example.com",
    "nps_score": 9
  }'
```

---

## Common Errors

### 1. Missing File: shiftops-user-explanations.php

**Error:**

```
Fatal error: Class 'ShiftOpsUserExplanations' not found
```

**Solution:**

- File has been created: `v2/api/shiftops-user-explanations.php`
- Ensure it's included in `shiftops.php` constructor
- Check file permissions (should be readable)

**Status:** ✅ FIXED

---

### 2. Database Error: Unknown database 'wp_www'

**Error:**

```
Es ist ein Fehler aufgetreten: Unknown database 'wp_www'
```

**Solution:**

- This is a WordPress database error, not related to ShiftOps
- Can be ignored as per user instructions
- ShiftOps doesn't use WordPress database

**Status:** ✅ EXPECTED (IGNORE)

---

### 3. Cache Not Working

**Symptoms:**

- Every request generates new analysis
- Cache files not being created
- Slow response times

**Solution:**

1. Check cache directory exists: `v2/cache/systems/shiftops/`
2. Verify directory permissions (755 or 777)
3. Check disk space availability
4. Review cache key generation logic
5. Check cache TTL (should be 3600 seconds)

**Debug:**

```php
// Add to shiftops.php
error_log("Cache key: " . $cacheKey);
error_log("Cache file: " . $cacheFile);
error_log("Cache exists: " . (file_exists($cacheFile) ? 'yes' : 'no'));
```

---

### 4. Google Places API Errors

**Error:**

```
Google Places API error: Request denied
```

**Solution:**

1. Check API key is valid: `AIzaSyAcmDVIEYDtmeXjKfIsqYxbNy2FFsluCdo`
2. Verify API key has Places API enabled
3. Check quota limits (1000 requests/day free tier)
4. Review rate limiting in code
5. Check API key restrictions (should allow referrer)

**Debug:**

```php
// Check API key in shiftops.php
error_log("Google API Key: " . substr($this->googleApiKey, 0, 10) . "...");
```

---

### 5. HubSpot Submission Failures

**Error:**

```
HubSpot submission failed: HTTP 400
```

**Solution:**

1. Verify HubSpot form ID: `41d07332-6697-4daa-b27e-dd60515f9c0f`
2. Check portal ID: `145133546`
3. Ensure email is valid format
4. Review form field mappings
5. Check HubSpot API token (if using private app)

**Debug:**

```php
// Check logs/shiftops-hubspot.log
// Check logs/shiftops-leads-full.log
```

---

### 5b. HubSpot Gating Form Submissions Not Appearing

**Error:**

```
User submitted unlock form with consent but contact/activity not visible in HubSpot
```

**Solution:**

1. **Check consent logging** – `shiftops-hubspot.php` logs consent at DEBUG level:
   - `hubspotConsent`: true/false (from request)
   - `hubspot_form_consent_in_payload`: yes/no/not_sent
   - `hubspot_status`: sent | skipped
   - Search logs for `ShiftOps gating form consent check` and `HubSpot skipped (no consent)` or `HubSpot submission successful`

2. **Run HubSpot contact debug script** – Query contact by email to verify form submissions:
   ```bash
   php v2/scripts/dev-helpers/debug-hubspot-contact.php test@example.com
   ```
   Requires `HUBSPOT_API_TOKEN` in environment or `v2/config/hubspot-api-key.php`.

3. **Form GUID reference:**
   - ShiftOps Report (gating): `41d07332-6697-4daa-b27e-dd60515f9c0f`
   - ShiftOps NPS: `804459f7-c18d-4da6-8a0b-a81f44bb8275`

4. **Common causes:**
   - Consent false: User did not check form checkbox and cookie consent (HubSpot) was not granted
   - `loadConsent` undefined: Footer scripts not yet loaded when form submitted (rare)
   - **Honeypot triggered:** If `website` field contains the business website (legacy bug), backend rejects as spam. Frontend must send `website` = honeypot value (empty) and `shiftops_business_website` = business website.
   - HubSpot may filter `example.com` test emails

---

### 6. Weather API Failures

**Error:**

```
Weather API error: Connection timeout
```

**Solution:**

1. Open-Meteo API is free tier (no API key needed)
2. Check internet connectivity
3. Verify coordinates are valid (lat: -90 to 90, lng: -180 to 180)
4. Review timeout settings (5 seconds)
5. Fallback weather data should be used

**Debug:**

```php
// Check weather API response
error_log("Weather API URL: " . $apiUrl);
error_log("Weather API Response: " . $response);
```

---

### 7. Customer Matching Not Working

**Error:**

```
Customer match not found (should match)
Call to undefined function mb_strtolower()
```

**Solutions:**

**Issue 1: mbstring Extension Missing (Production)**

**Error:** `Call to undefined function mb_strtolower()`

**Solution:**

- Fixed in `shiftops-customer-matcher.php` with `safeStrToLower()` helper
- Falls back to `strtolower()` if `mb_strtolower()` not available
- All `mb_strtolower()` calls replaced with `safeStrToLower()`

**Status:** ✅ FIXED

**Issue 2: Empty Customer List**

**Error:** Customer list loads but is empty

**Solution:**

- Fixed lazy loading bug: explicitly checks `count($this->customers) > 0`
- Added retry logic if list was previously loaded but found empty
- Validates JSON structure before using

**Status:** ✅ FIXED

**Issue 3: Path Resolution**

**Error:** Customer file not found in production

**Solution:**

- Implemented robust path resolution with multiple fallbacks:
  1. Explicitly provided path
  2. From ORDIO_CUSTOMERS_FILE constant
  3. Relative to current file
  4. Absolute path using realpath()
  5. DOCUMENT_ROOT based path
- Logs all path attempts for debugging

**Status:** ✅ FIXED

**Issue 4: Domain Matching with Unusual TLDs**

**Error:** Domains like `lafonda.koeln` not matching

**Solution:**

- Improved domain extraction to handle unusual TLDs (.koeln, .berlin, .bayern)
- Handles domain variants (www vs non-www, http vs https, trailing slash)
- Domain indexing for faster exact matches

**Status:** ✅ FIXED

**Debug:**

```php
// Use health check endpoint
curl https://www.ordio.com/v2/api/shiftops-health-check.php

// Use test matching endpoint
curl "https://www.ordio.com/v2/api/shiftops-test-matching.php?name=La+Fonda&website=https://lafonda.koeln"
```

**Logging:**

- All matching attempts logged with request IDs
- Character-by-character comparison available in test endpoint
- Performance metrics logged (load time, memory usage)

---

### 8. Score Calculation Errors

**Error:**

```
Pillar scores sum doesn't match total score
```

**Solution:**

1. Review `adjustPillarScoresToMatchTotal()` function
2. Check rounding logic (floor vs round)
3. Verify customer boost calculations
4. Review data completeness multiplier
5. Check score capping (max 95 for customers)

**Debug:**

```php
// Add to calculateShiftOpsScore()
error_log("Raw scores: " . json_encode($rawScores));
error_log("Raw total: " . $rawTotalScore);
error_log("Adjusted total: " . $adjustedScore);
error_log("Final total: " . $finalTotalScore);
error_log("Adjusted scores: " . json_encode($adjustedScores));
```

---

### 9. localStorage Issues

**Error:**

```
localStorage quota exceeded
```

**Solution:**

1. Optimize data before storing (see `optimizeAnalysisData()`)
2. Truncate weather forecast to 7 days
3. Filter holidays to next 30 days
4. Remove redundant competitor data
5. Check localStorage size before storing

**Debug:**

```javascript
// Check localStorage size
const dataSize = JSON.stringify(data).length;
console.log(`Data size: ${(dataSize / 1024).toFixed(1)}KB`);
console.log(
  `localStorage usage: ${(JSON.stringify(localStorage).length / 1024).toFixed(
    1
  )}KB`
);
```

---

### 10. PDF Export Failures

**Error:**

```
PDF generation failed: html2canvas error
```

**Solution:**

1. Check html2canvas library is loaded
2. Verify jsPDF library is loaded
3. Check CORS settings for images
4. Review PDF generator code
5. Test with simpler HTML first

**Debug:**

```javascript
// Check libraries
console.log("html2canvas:", typeof html2canvas);
console.log("jsPDF:", typeof window.jspdf);
```

---

## Performance Optimization

### 1. Reduce API Calls

**Strategy:**

- Use caching (1-hour TTL)
- Batch requests where possible
- Reuse cached data
- Optimize cache key generation

**Implementation:**

```php
// Check cache before API call
$cachedResult = $this->getCachedAnalysis($cacheKey);
if ($cachedResult) {
    return $cachedResult;
}
```

---

### 2. Optimize Database Queries

**Strategy:**

- No database queries (file-based)
- Use file caching
- Optimize JSON encoding/decoding
- Minimize file I/O operations

---

### 3. Reduce Frontend Load

**Strategy:**

- Two-phase loading (essential → enhanced)
- Progressive rendering
- Lazy load images
- Optimize localStorage usage

**Implementation:**

```javascript
// Essential mode first
const essentialData = await fetchEssentialData(place);
displayEssentialData(essentialData);

// Enhanced mode in background
const enhancedData = await fetchEnhancedData(place);
updateWithEnhancedData(enhancedData);
```

---

### 4. Optimize Calculations

**Strategy:**

- Cache expensive calculations
- Use efficient algorithms
- Minimize nested loops
- Pre-compute industry benchmarks

---

## Debugging Steps

### 1. Enable Error Logging

**PHP:**

```php
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', __DIR__ . '/../logs/shiftops-debug.log');
error_reporting(E_ALL);
```

**JavaScript:**

```javascript
console.log("Debug info:", data);
console.error("Error:", error);
```

---

### 2. Check Log Files

**Log Files:**

- `logs/shiftops-debug.log` - PHP errors
- `logs/shiftops-hubspot.log` - HubSpot submissions (includes consent check logs)
- `logs/shiftops-leads-full.log` - Rich lead data
- `logs/shiftops-benchmarks.log` - Benchmark data

**HubSpot Contact Debug Script:**

```bash
php v2/scripts/dev-helpers/debug-hubspot-contact.php <email>
```

**Location:**

- `v2/logs/` (relative to project root)

---

### 3. Test API Endpoints

**Test Essential Mode:**

```bash
curl -X POST http://localhost:8003/v2/api/shiftops.php \
  -H "Content-Type: application/json" \
  -d '{"mode":"essential","place_id":"ChIJN1t_tDeuEmsRUsoyG83frY4","name":"Test"}'
```

**Test Enhanced Mode:**

```bash
curl -X POST http://localhost:8003/v2/api/shiftops.php \
  -H "Content-Type: application/json" \
  -d '{"mode":"enhanced","place_id":"ChIJN1t_tDeuEmsRUsoyG83frY4","name":"Test"}'
```

---

### 4. Verify File Permissions

**Required Permissions:**

- Cache directory: `755` or `777`
- Log directory: `755` or `777`
- Customer list file: `644` (readable)
- Config files: `644` (readable)

**Check:**

```bash
ls -la v2/cache/systems/shiftops/
ls -la v2/logs/
ls -la v2/data/ordio-customers.json
```

---

### 5. Monitor Performance

**Metrics to Track:**

- API response time
- Cache hit rate
- Memory usage
- File I/O operations
- localStorage size

**Tools:**

- Browser DevTools (Network tab)
- PHP error logs
- Server logs
- Performance monitoring tools

---

## Common Issues by Component

### Frontend (shiftops.php)

**Issue:** Google Places autocomplete not working

- Check Google Places API key
- Verify libraries are loaded
- Check browser console for errors

**Issue:** Analysis not loading

- Check API endpoint is accessible
- Verify request format
- Check response format

**Issue:** Report page not showing data

- Check localStorage data exists
- Verify data format is correct
- Check for JavaScript errors

---

### Backend (shiftops.php API)

**Issue:** Analysis taking too long

- Check cache is working
- Review external API calls
- Optimize calculations

**Issue:** Scores seem incorrect

- Review scoring algorithm
- Check data completeness
- Verify customer matching

**Issue:** Cache not working

- Check directory permissions
- Verify cache key generation
- Review cache TTL

---

### Integrations

**Issue:** Google Places API errors

- Check API key validity
- Verify quota limits
- Review rate limiting

**Issue:** HubSpot submission failures

- Check form ID and portal ID
- Verify email format
- Review field mappings

**Issue:** Weather API failures

- Check internet connectivity
- Verify coordinates are valid
- Review fallback logic

---

## Best Practices

1. **Always Check Logs First**

   - Most errors are logged
   - Check error log files
   - Review browser console

2. **Test in Development First**

   - Use localhost:8003
   - Test with known businesses
   - Verify all components work

3. **Monitor Performance**

   - Track response times
   - Monitor cache hit rates
   - Check memory usage

4. **Handle Errors Gracefully**

   - Provide user-friendly messages
   - Use fallback data when possible
   - Log errors for debugging

5. **Keep Dependencies Updated**
   - Check for library updates
   - Review API changes
   - Test after updates

---

## Getting Help

If you encounter issues not covered here:

1. Check error logs
2. Review code documentation
3. Test with known working data
4. Verify all dependencies are loaded
5. Check file permissions and paths

For specific issues, refer to:

- `SHIFTOPS_ARCHITECTURE.md` - System architecture
- `SHIFTOPS_API_DOCUMENTATION.md` - API documentation
- Code comments in source files
