# PocketBase Timestamp + convertToPB Guard

## The problem

The `pocketbase.js` adapter exports a `Timestamp` class for Firebase API compatibility. Three interlocking bugs make writes silently fail when Timestamps are used without proper support infrastructure.

## Root causes

### 1. Timestamp static methods don't exist by default

Firebase APIs like `Timestamp.fromDate(date)` and `Timestamp.now()` are NOT present on the PocketBase adapter's `Timestamp` class. Any code that calls them (e.g., `handleEditAppointment` in `appointments.js`) throws `TypeError: Timestamp.fromDate is not a function`.

Because async event handlers don't surface their rejections to the browser UI, the user sees **nothing** — no error, no notification. The function just silently dies.

**Fix — add to the Timestamp class in `pocketbase.js`:**
```js
static fromDate(date) { return new Timestamp(date); }
static now() { return new Timestamp(new Date()); }
```

### 2. `convertToPB` doesn't recognize Timestamp objects

`convertToPB` checks `instanceof Date` to serialize dates to ISO strings. But `Timestamp` does NOT extend `Date` — it wraps a `_date` property. So Timestamp objects fall through to the generic `JSON.stringify()` branch and produce garbage like `{"_date":"2026-06-15T..."}`. PocketBase either rejects this or stores junk that `convertFromPB` can't parse back.

**Fix — add Timestamp check BEFORE the generic object branch in `convertToPB`:**
```js
} else if (value && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Timestamp)) {
    result[key] = JSON.stringify(value);
} else if (value instanceof Timestamp) {
    result[key] = value.toISOString();
```

### 3. `new Date(timestampObj)` returns Invalid Date

Since Timestamp is not a Date, `new Date(timestampObj)` produces an `Invalid Date`. This breaks code that reads from PocketBase (where `convertFromPB` created Timestamp objects) and tries to display or process dates.

**Fix — always use `.toDate()`:**
```js
const date = obj?.toDate ? obj.toDate() : new Date(obj);
```

## Preferred pattern for new writes

For new records and updates, **prefer plain `new Date()` over `Timestamp.fromDate()`**. `convertToPB` handles `Date` instances natively at the `instanceof Date` branch. Timestamp is only needed for reading FROM PocketBase (to preserve the Firebase-compatible API surface).

```js
// BAD — Timestamp chain of pain
appointmentDateTime: Timestamp.fromDate(new Date(...)),
updatedAt: Timestamp.now()

// GOOD — plain Date, convertToPB handles it
appointmentDateTime: new Date(...),
updatedAt: new Date()
```

## Async handler "nothing happens" pattern

When a button click produces zero visible effect, the most common cause is an uncaught synchronous throw in an async handler before the first `await`. The fix:

```js
async function handleSave() {
    console.log('=== SAVE CLICKED ===');  // proves handler fired
    try {
        // ALL code — validation, DOM reads, API calls — inside one try
        await updateDoc(...);
    } catch (error) {
        console.error('Save error:', error);
        alert('Save failed: ' + (error.message || error));  // hard fallback
        showNotification('Failed to save.', true);
    }
}
```

No nested try/catch — one try wrapping the entire body.

## Files affected

- `pocketbase.js` — `Timestamp` class, `convertToPB` function, `TIMESTAMP_FIELDS` set
- `appointments.js` — `handleEditAppointment`, date reading in `openEditAppointmentModal`, date grouping
- Any file using `Timestamp.fromDate()` or `Timestamp.now()`
- Any file doing `new Date(someTimestampObject)` on PocketBase-loaded data

## TIMESTAMP_FIELDS Maintenance Rule

The `TIMESTAMP_FIELDS` set in `pocketbase.js` controls which ISO string fields `convertFromPB` auto-converts to `Timestamp` objects on retrieval. **When you add a new date/datetime field to ANY PocketBase collection, you MUST also add its field name to this set.** Missing fields come back as raw ISO strings, which break `.toDate()` calls and cause `new Date()` fallbacks (producing today's date instead of the stored date).

Current set (as of June 2026):
```
writeupTime, createdAt, updatedAt, lastUpdated,
appointmentDateTime, completionTime, lastActivity,
timestamp, dateCreated, startDate, endDate, deadline,
dueDateTime, promisedTime
```

`dueDateTime` and `promisedTime` were added June 2026 after tasks showed overdue with today's date because these fields were missing from both the PocketBase schema AND `TIMESTAMP_FIELDS`.
