# ShopProQuote JS Debugging Patterns

## Silent module load failure from SyntaxError

**Symptom:** A feature is "stuck loading" despite the HTML being correct and the backend responding. The page shows a permanent "Loading..." state.

**Root cause:** A syntax error in a `<script>`-loaded JS file prevents the ENTIRE module from executing. Common in ShopProQuote because JS files are loaded as regular scripts (not ES modules with isolated scopes).

**Example from dashboard briefing (June 2026):**
```js
// Line ~3926
const briefingText = dsResult.choices?.[0]?.message?.content?.trim() || '';
// ... later, line ~3944, same function scope:
const briefingText = contentDiv.textContent || '';  // 💥 SyntaxError: redeclaration of const
```

JavaScript refuses to parse the file, `window.generateDailyBriefing` is never defined, the inline fallback in `index.html` polls forever for it, and the user sees "Loading briefing..." permanently.

## Debugging steps

1. **Check if the function exists at all:**
   ```js
   // In browser console
   typeof window.generateDailyBriefing  // 'undefined' = module never loaded
   ```

2. **Syntax-check the file (if Node.js available):**
   ```bash
   node -c dashboard.js
   ```

3. **Without Node.js, grep for duplicate declarations:**
   ```bash
   # Find duplicate const declarations in the same function
   sed -n '/async function generateDailyBriefing/,/^    }/p' dashboard.js | grep -P "^\s*const \w+ =" | awk '{print $2}' | sort | uniq -d
   ```

4. **Check browser console** — a SyntaxError appears but users often miss it.

## Proxy verification pattern

When an AI feature relies on an nginx proxy, verify the proxy works independently of the frontend:

```bash
curl -k -X POST https://127.0.0.1/deepseek/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"deepseek-v4-flash","messages":[{"role":"user","content":"test"}],"max_tokens":10}'
```

This separates "proxy broken" from "JS broken."

## "Button does nothing" — element existence guard pattern

When clicking a submit button produces literally zero visible effect (no spinner, no error, no console message beyond a hidden TypeError), the most likely cause is that one or more DOM elements the handler reads from are `null`. The form element exists in the HTML, but at the time the handler fires, it's not in the DOM (dynamic insertion timing, modal re-opening, or form.reset() clearing dynamic content).

### Defensive pattern

Instead of directly chaining `.value` on `document.getElementById()` calls, capture each element reference first, check for null, and log which ones are missing:

```js
async function handleCreateTask() {
    console.log('🔍 handleCreateTask() called');
    const titleEl = document.getElementById('new-task-title');
    const dueDateEl = document.getElementById('new-task-due-date');
    const dueTimeEl = document.getElementById('new-task-due-time');
    
    if (!titleEl || !dueDateEl || !dueTimeEl) {
        console.error('❌ Missing form elements:', {
            title: !!titleEl, dueDate: !!dueDateEl, dueTime: !!dueTimeEl
        });
        showNotification('Form elements not found. Please refresh the page.', true);
        return;
    }
    
    const title = titleEl.value.trim();
    // ... rest of handler
}
```

This pattern accomplishes three things simultaneously:
1. The `console.log` at line 1 proves the handler even fired (vs HTML5 validation blocking submit)
2. The null checks pinpoint exactly which element is missing
3. The early return with notification gives the user a visible message instead of silent failure

### Why this beats try/catch alone

A `try/catch` wrapping the handler body catches the TypeError, but you don't know WHICH element caused it without the logged boolean map. And you can't show a meaningful error message — just "something went wrong."
