# Common Bugs & Pitfalls

## Auth redirect loop (index.html ↔ dashboard.html)

**Symptom**: Page constantly refreshes between index.html and dashboard.html.

**Root cause**: `index.html` line 131 blindly checks `localStorage.getItem('pocketbase_auth')` — any truthy value (including stale/expired tokens) triggers a redirect to dashboard.html. Dashboard's `onAuthStateChanged` then detects the invalid token and bounces back to index.html, but the stale token is never cleared from localStorage.

**Fix**: In every file that redirects unauthenticated users back to index.html, clear the stale token first:

```javascript
localStorage.removeItem('pocketbase_auth');
window.location.href = 'index.html';
```

Files that need this: `dashboard.js` (line 122), `appointments.js` (line 50), `repair-orders.js` (line 58).

## PocketBase auto-cancellation ("request was auto-cancelled")

**Symptom**: Creating/updating records fails with "The request was auto-cancelled" or similar abort error.

**Root cause**: PocketBase SDK auto-cancels requests that share the same `requestKey`. By default, all `create`/`update` calls to the same collection share the same key — a rapid double-click fires two identical requests and the first is aborted.

**Fix — two parts**:

1. Disable auto-cancellation in `pocketbase.js` by passing `{ requestKey: null }` to all mutating PocketBase calls:
   - `addDoc`: `pb.collection(collName).create(pbData, { requestKey: null })`
   - `updateDoc`: `pb.collection(collName).update(docId, pbData, { requestKey: null })`
   - `setDoc`: both internal `update` and `create` calls need it

2. Add double-submission guard to form submit handlers — disable the button on first click:
```javascript
const submitBtn = document.getElementById('submit-create-ro');
if (submitBtn && submitBtn.disabled) return;
if (submitBtn) {
    submitBtn.disabled = true;
    submitBtn.textContent = 'Creating...';
}
```
Re-enable the button in BOTH the success path (after form clear/collapse) AND the catch block on error. Forgetting the success path leaves the button permanently disabled after a successful submission.

## DuckDNS DNS stale / site unreachable

**Symptom**: "This site can't be reached" when accessing grajmedia.duckdns.org.

**Check order**:
1. `dig +short grajmedia.duckdns.org` — if returns `127.0.0.1`, run the DuckDNS updater manually: `bash /opt/duckdns/update.sh`
2. The cron job runs every 5 min: `*/5 * * * * /opt/duckdns/update.sh`
3. `/etc/hosts` has `127.0.0.1 grajmedia.duckdns.org` for local hairpin NAT — this is intentional, not a bug

## Dashboard Completed counter shows 0 (`ro.status` vs `ro.workStatus`)

**Symptom**: "Completed: 0" on the dashboard Repair Orders Today card, despite having completed orders today.

**Root causes (usually BOTH apply)**:

1. **Wrong field**: Code filters by `ro.status === 'completed'` but `status` only stores `waiter`/`drop-off`. Must use `ro.workStatus === 'completed'`.
2. **Completed orders filtered out at source**: `loadRepairOrders()` in dashboard.js removes all completed orders from `dashboardData.repairOrders` before any counter can see them.

**Fix checklist**:
- Replace ALL `ro.status === 'completed'` with `ro.workStatus === 'completed'`
- Replace ALL `ro.status === 'picked-up'` with `ro.workStatus === 'waiting-for-pickup'`
- Remove the completed-filter from `loadRepairOrders()` so `dashboardData.repairOrders` contains ALL orders
- Each consumer then filters by `ro.workStatus` as needed
- See `references/status-field-confusion.md` for the full reference
