Skip to content

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

ProjectStackFilesEntities Searched
Sales MCExpress + SQLite + React/Vite + Tailwind v44 filessalespeople, cw_accounts, import_intel, trade_lanes
HandyMannyNext.js 16 + Prisma + PostgreSQL + Tailwind v44 filesshipments, companies, contacts

Sales MC Version (JS, Express, SQLite)

Files:

FilePurpose
server/app.jsGET /api/search?q=&cat= — raw SQL LIKE queries across 4 tables
client/src/hooks/useSearch.jsDebounced hook, AbortController, localStorage
client/src/components/UniversalSearch.jsxFull UI with framer-motion animations, lucide-react icons
client/src/components/NavBar.jsxIntegration 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:

FilePurpose
src/app/api/search/route.tsGET /api/search?q=&cat= — Prisma queries with auth + role scoping
src/hooks/useSearch.tsSame pattern as Sales MC, TypeScript
src/components/layout/universal-search.tsxCSS transitions (no framer-motion), emoji icons, var(--brand) colors
src/components/layout/topbar.tsxIntegration 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

  1. Backend: Add GET /api/search?q=&cat= — search relevant tables, return { results: { cat1: [...], cat2: [...] }, total, query }
  2. Hook: Copy useSearch — only change STORAGE_KEY and empty results shape
  3. Component: Copy UniversalSearch — update CATEGORIES array, getResultDisplay(), QuickViewPanel, and NAV_TARGETS
  4. 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

HandyManny Documentation System