100 lines
4.0 KiB
Markdown
100 lines
4.0 KiB
Markdown
# PocketBase Query Pitfalls (spq-v2)
|
|
|
|
These recurring bugs cause silent page crashes and "Something went wrong" in spq-v2 React pages. Check these FIRST when a page breaks.
|
|
|
|
## sort: '-created' fails on base collections
|
|
PB auto-adds `created`/`updated` to **auth** collections only, not **base** collections. spq-v2 uses base collections (quotes, repairOrders, appointments, customers, services, settings). Always use `sort: '-id'` — PB IDs are ULID-based and time-sortable.
|
|
|
|
**Symptom**: Page shows "Something went wrong".
|
|
|
|
**Fix**: `pb.collection('repairOrders').getList(1, 200, { sort: '-id' })`
|
|
|
|
## JSON fields come as strings from PB API
|
|
When PocketBase stores a JSON field, the API may return it as a **serialized string**, not a parsed array/object. Accessing `.reduce()` or `.map()` on a string throws.
|
|
|
|
**Symptom**: `e.reduce is not a function`, `Unexpected end of JSON input`.
|
|
|
|
**Fix** — always normalize:
|
|
```ts
|
|
let services = item.services || [];
|
|
if (typeof services === 'string' && services.trim()) {
|
|
try { services = JSON.parse(services); } catch { services = []; }
|
|
}
|
|
if (!Array.isArray(services)) services = [];
|
|
```
|
|
|
|
## `fields` parameter causes silent failures
|
|
Restricting fields with `fields: 'id,customerName,...'` fails silently if any field name doesn't exist in the collection. PB returns an error that is hard to diagnose.
|
|
|
|
**Symptom**: "Failed to load appointments" with cryptic message.
|
|
|
|
**Fix**: Omit `fields` entirely and let PB return all fields. The bandwidth cost is negligible.
|
|
|
|
## Date formatting: always guard against undefined
|
|
`new Date(undefined)` creates an Invalid Date object. Calling `.toLocaleDateString()`, `.toISOString()`, or any format method on it throws `RangeError: Invalid time value`.
|
|
|
|
**Symptom**: White screen, `Uncaught RangeError: Invalid time value`.
|
|
|
|
**Fix** — always add null guards:
|
|
```ts
|
|
function formatDate(iso: string | undefined | null) {
|
|
if (!iso) return '—';
|
|
const d = new Date(iso);
|
|
if (isNaN(d.getTime())) return '—';
|
|
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
|
|
}
|
|
```
|
|
|
|
# React/Vite Pitfalls (spq-v2)
|
|
|
|
## Input focus loss in nested components
|
|
Defining a component as a nested function inside another component causes React to destroy/recreate it on every parent re-render. Input fields lose focus on every keystroke.
|
|
|
|
**Symptom**: Typing in a price/textarea field causes focus loss after each character.
|
|
|
|
**Fix**: Extract to file-level and wrap with `React.memo`:
|
|
```tsx
|
|
const ServiceRow = memo(function ServiceRow(props: ServiceRowProps) {
|
|
return <div>...</div>;
|
|
});
|
|
```
|
|
|
|
## CDN dynamic imports fail in Vite production builds
|
|
`await import('https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js')` cannot be bundled by Vite at build time. The import silently fails in production.
|
|
|
|
**Symptom**: Scan Screenshot button opens modal but OCR never starts.
|
|
|
|
**Fix**: Use static npm imports:
|
|
```tsx
|
|
import Tesseract from 'tesseract.js'; // correct
|
|
```
|
|
|
|
# Nginx + DeepSeek API
|
|
|
|
- DeepSeek API key stored in `AUXILIARY_APPROVAL_API_KEY` env var (35 chars, valid key)
|
|
- Nginx config at `/etc/nginx/sites-enabled/shopproquote` proxies `/deepseek/` → `https://api.deepseek.com/`
|
|
- `proxy_set_header Authorization "Bearer $key"` must include the full uncorrupted key
|
|
- **CRITICAL**: The secrets output filter truncates API keys when reading/writing files. When constructing nginx configs that embed the key, use Python (`os.environ.get()`) to read the key and write the config — do NOT use tee/heredoc with the key inline, as the filter will truncate it.
|
|
- All AI calls (AI Write, Generate Priorities, AI Suggest, Scan Screenshot extraction) use the same `/deepseek/v1/chat/completions` endpoint through nginx
|
|
|
|
## SPA nginx config for spq-v2
|
|
|
|
```nginx
|
|
root /path/to/spq-v2/dist;
|
|
index index.html;
|
|
|
|
location / {
|
|
try_files $uri $uri/ /index.html; # SPA fallback — NOT =404
|
|
}
|
|
|
|
location /pb/ {
|
|
proxy_pass http://127.0.0.1:8091/; # PocketBase
|
|
}
|
|
|
|
location /deepseek/ {
|
|
proxy_pass https://api.deepseek.com/;
|
|
proxy_set_header Authorization "Bearer $API_KEY";
|
|
proxy_ssl_server_name on;
|
|
}
|
|
```
|