Security Notice
This document has been auto-published from the internal knowledge base. All passwords, API keys, tokens, and IP addresses have been redacted for security.
Reusable Components & Patterns
Universal Search (Ctrl+K)
What It Does
Full-text search bar in the navbar/topbar with categorized results, quick view preview panel, keyboard navigation, and deep-link navigation. Ctrl+K shortcut to focus.
Features
- Debounced search (300ms) with AbortController for stale requests
- Category filter pills (search one table or all)
- Grouped results with highlighted matching text
- Quick View panel (right side, 280-320px) with entity-specific fields
- Keyboard nav: arrows navigate, → opens quick view, ← closes, Enter navigates, Esc closes
- Recent searches in localStorage (max 8)
- Status/stage badges per entity
Implementations
| Project | Stack | Files | Entities Searched |
|---|---|---|---|
| Sales MC | Express + SQLite + React/Vite + Tailwind v4 | 4 files | salespeople, cw_accounts, import_intel, trade_lanes |
| HandyManny | Next.js 16 + Prisma + PostgreSQL + Tailwind v4 | 4 files | shipments, companies, contacts |
Sales MC Version (JS, Express, SQLite)
Files:
| File | Purpose |
|---|---|
server/app.js | GET /api/search?q=&cat= — raw SQL LIKE queries across 4 tables |
client/src/hooks/useSearch.js | Debounced hook, AbortController, localStorage |
client/src/components/UniversalSearch.jsx | Full UI with framer-motion animations, lucide-react icons |
client/src/components/NavBar.jsx | Integration point (between logo and nav links) |
Backend Pattern (SQLite):
js
const like = `%${term}%`;
results.team = db.prepare(`
SELECT id, name, email, team, territory, cw_rep_code
FROM salespeople
WHERE active = 1 AND (name LIKE ? OR email LIKE ? OR territory LIKE ?)
ORDER BY name LIMIT 10
`).all(like, like, like);Dependencies used: framer-motion (AnimatePresence), lucide-react (Search, X, Users, Building2, Globe, Navigation, etc.)
Response format:
json
{ "results": { "team": [...], "accounts": [...], "import_intel": [...], "trade_lanes": [...] }, "total": 25, "query": "term" }HandyManny Version (TypeScript, Next.js, Prisma)
Files:
| File | Purpose |
|---|---|
src/app/api/search/route.ts | GET /api/search?q=&cat= — Prisma queries with auth + role scoping |
src/hooks/useSearch.ts | Same pattern as Sales MC, TypeScript |
src/components/layout/universal-search.tsx | CSS transitions (no framer-motion), emoji icons, var(--brand) colors |
src/components/layout/topbar.tsx | Integration point (replaced empty <div />) |
Backend Pattern (Prisma):
ts
const shipments = await prisma.shipment.findMany({
where: {
archivedAt: null,
OR: [
{ mbl: { contains: q, mode: "insensitive" } },
{ trackingCode: { contains: q, mode: "insensitive" } },
{ containers: { some: { containerNumber: { contains: q, mode: "insensitive" } } } },
],
...(isCustomer && companyId ? { companyShipments: { some: { companyId } } } : {}),
},
include: { containers: { select: { containerNumber: true } } },
take: 10,
orderBy: { updatedAt: "desc" },
});Key differences from Sales MC:
- No extra dependencies (no framer-motion, no lucide-react)
- Uses CSS transitions + emoji icons to match existing codebase
- Brand-aware via
var(--brand)CSS variable (auto red/blue per tenant) - Auth-gated: customers only see their own company's shipments
- Companies/contacts hidden from customer roles
- Spanish labels (Embarques, Clientes, Contactos, Recientes)
- Shipment click deep-links to
/shipments/[id]
How to Add to a New Project
- Backend: Add
GET /api/search?q=&cat=— search relevant tables, return{ results: { cat1: [...], cat2: [...] }, total, query } - Hook: Copy
useSearch— only changeSTORAGE_KEYand empty results shape - Component: Copy
UniversalSearch— updateCATEGORIESarray,getResultDisplay(),QuickViewPanel, andNAV_TARGETS - Integration: Import into navbar/topbar, place where there's room
Adaptation Checklist
- [ ] Define categories (key, label, icon, color)
- [ ] Define result display per category (title, subtitle, badge)
- [ ] Define quick view fields per category
- [ ] Define navigation targets per category
- [ ] Choose icon system (lucide-react if available, emoji if minimal deps)
- [ ] Choose animation (framer-motion if available, CSS transitions if not)
- [ ] Add auth/role scoping if needed
- [ ] Set Spanish or English labels