61 lines
2.7 KiB
Markdown
61 lines
2.7 KiB
Markdown
# SPQ v2 Development Pitfalls
|
|
|
|
Common recurring issues when working on the React/Vite frontend.
|
|
|
|
## PocketBase Collection Naming
|
|
|
|
PocketBase collections use **camelCase** by convention (`repairOrders`, not `repair_orders`). Always verify the actual name:
|
|
|
|
```bash
|
|
curl -s http://127.0.0.1:8091/api/collections/NAME/records?perPage=1 \
|
|
-H "Authorization: TOKEN"
|
|
# 404 = collection doesn't exist with that name
|
|
# 400 with "Something went wrong" = collection exists but query is broken (wrong field/sort)
|
|
# 200 = correct name, query works
|
|
```
|
|
|
|
## sort: '-created' Returns 400
|
|
|
|
Most SPQ PocketBase collections were created without system `created`/`updated` fields. Queries using `sort: '-created'` fail with HTTP 400.
|
|
|
|
**Fix**: Use `sort: '-id'` everywhere unless you've confirmed the `created` field exists on that specific collection. PB IDs are time-sortable (they're ordered by creation time).
|
|
|
|
Affected collections: `quotes`, `repairOrders`, any collection created via API without explicit schema.
|
|
|
|
## React Input Focus Loss with Zustand
|
|
|
|
When a React component with input fields is defined as a **nested function inside its parent component**, every zustand store update causes a full parent re-render. This creates a new function identity for the child component, React sees it as a different component type, and destroys/recreates the DOM — killing input focus.
|
|
|
|
**Fix**: Extract the component to a file-level function, wrap in `memo`, and pass all state/actions as explicit props:
|
|
|
|
```tsx
|
|
// WRONG — loses focus on every keystroke
|
|
function Parent() {
|
|
const update = useStore(s => s.update);
|
|
const Child = ({ item }) => (
|
|
<input value={item.name} onChange={e => update({ name: e.target.value })} />
|
|
);
|
|
}
|
|
|
|
// RIGHT — focus stays
|
|
interface ChildProps { item: Item; onUpdate: (u: Partial<Item>) => void; }
|
|
const Child = memo(function Child({ item, onUpdate }: ChildProps) {
|
|
return <input value={item.name} onChange={e => onUpdate({ name: e.target.value })} />;
|
|
});
|
|
```
|
|
|
|
## AI Features Return No Results
|
|
|
|
The 3 AI functions in `src/lib/ai.ts` (`aiWriteExplanation`, `getPriorityAnalysis`, `aiSuggestServices`) call `/deepseek/v1/chat/completions`. This path must be proxied to `https://api.deepseek.com/` with the API key.
|
|
|
|
The proxy script template at `references/local-testing.md` includes this. Key verification:
|
|
|
|
```bash
|
|
curl -s -X POST http://localhost:4173/deepseek/v1/chat/completions \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"model":"deepseek-v4-flash","messages":[{"role":"user","content":"hi"}],"temperature":0,"thinking":{"type":"disabled"},"max_tokens":50}'
|
|
# Should return {"choices":[{"message":{"content":"Hello!"}}]}
|
|
```
|
|
|
|
API key is extracted from `/etc/nginx/sites-enabled/shopproquote` and stored in `/tmp/deepseek_key.txt`.
|