Frontend Omni-Search Specification

# Frontend Omni-Search Specification

**FID**: hex-bridge **Gap**: omni-search-spec **Realm**: left_hand

## Purpose

Replace Bridge theme's default search with a multi-entity omni-search that searches across Products, Blog Posts, Pages, and My Account sections from a single input.

## Entities & Search Fields

| Entity | Post Type | Search Fields | Display | |--------|-----------|---------------|---------| | **Products** | `product` | title, SKU, short_description, categories | Image, title, price, category badge | | **Blog Posts** | `post` | title, content excerpt, categories, tags | Title, date, category badge | | **Pages** | `page` | title, content excerpt | Title, parent breadcrumb | | **My Account** | static | predefined links | Icon, label (Warranty, Tickets, Orders) |

## Architecture

``` ┌─────────────────────────────────────────────────────┐ │ Search Input (nav trigger: .search_slides_from_header_bottom) └───────────────────────┬─────────────────────────────┘ │ keyup (300ms debounce) ▼ ┌─────────────────────────────────────────────────────┐ │ AJAX: wp_ajax_hex_omni_search │ │ ├── search_products($query, $limit) │ │ ├── search_posts($query, $limit) │ │ ├── search_pages($query, $limit) │ │ └── filter_account_links($query) │ └───────────────────────┬─────────────────────────────┘ │ JSON response ▼ ┌─────────────────────────────────────────────────────┐ │ Results Overlay │ │ ├── Products section (collapsible) │ │ ├── Blog section (collapsible) │ │ ├── Pages section (collapsible) │ │ └── My Account section (static links) │ └─────────────────────────────────────────────────────┘ ```

## File Structure

``` hex-bridge/ ├── includes/ │ └── class-omni-search.php # Backend search logic ├── assets/ │ ├── js/ │ │ └── omni-search.js # Frontend handler │ └── css/ │ └── omni-search.css # Overlay styles └── functions.php # Include & enqueue ```

## Backend: `class-omni-search.php`

```php class Hex_Omni_Search { public function __construct() { add_action('wp_ajax_hex_omni_search', [$this, 'handle_search']); add_action('wp_ajax_nopriv_hex_omni_search', [$this, 'handle_search']); }

public function handle_search() { $query = sanitize_text_field($_POST['query'] ?? ''); $results = [ 'products' => $this->search_products($query, 5), 'posts' => $this->search_posts($query, 5), 'pages' => $this->search_pages($query, 5), 'account' => $this->filter_account_links($query), ]; wp_send_json_success($results); }

private function search_products($query, $limit) { // WP_Query with 's' param + meta_query for SKU // Return: id, title, permalink, price, image, categories }

private function search_posts($query, $limit) { // WP_Query for post type 'post' // Return: id, title, permalink, date, categories }

private function search_pages($query, $limit) { // WP_Query for post type 'page' // Return: id, title, permalink, parent_title }

private function filter_account_links($query) { // Static array of My Account links // Filter by query match in label $links = [ ['label' => 'Warranty & Registration', 'url' => '/warranty-and-registration/', 'icon' => 'fa-shield'], ['label' => 'My Support Tickets', 'url' => '/my-tickets/', 'icon' => 'fa-ticket'], ['label' => 'My Orders', 'url' => '/my-account/orders/', 'icon' => 'fa-shopping-bag'], ['label' => 'My Account', 'url' => '/my-account/', 'icon' => 'fa-user'], ]; return array_filter($links, fn($l) => stripos($l['label'], $query) !== false); } } ```

## Frontend: `omni-search.js`

```javascript class HexOmniSearch { constructor() { this.debounceTimer = null; this.overlay = null; this.init(); }

init() { // Intercept Bridge's search trigger document.querySelectorAll('.search_slides_from_header_bottom').forEach(btn => { btn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); this.openOverlay(); }); }); }

openOverlay() { // Create/show overlay with search input // Focus input }

handleInput(query) { clearTimeout(this.debounceTimer); if (query.length < 2) { this.clearResults(); return; } this.debounceTimer = setTimeout(() => this.search(query), 300); }

async search(query) { const response = await fetch(hexOmniSearch.ajaxUrl, { method: 'POST', body: new URLSearchParams({ action: 'hex_omni_search', query: query, nonce: hexOmniSearch.nonce }) }); const data = await response.json(); if (data.success) this.renderResults(data.data); }

renderResults(results) { // Render sections: Products, Posts, Pages, Account // Each section collapsible with count badge } } ```

## UI Design

- **Overlay**: Full-width slide-down (like Bridge default, but custom) - **Input**: Large, centered, with clear button - **Sections**: Collapsible accordions with result count - **Results**: Card-style with image/icon, title, meta - **Keyboard**: ESC closes, arrows navigate, Enter selects

## Integration Points

1. **Hook Bridge trigger**: Intercept `.search_slides_from_header_bottom` click 2. **Prevent default**: Stop Bridge's search panel from opening 3. **Enqueue**: Load JS/CSS only on frontend (not admin) 4. **Localize**: Pass ajaxUrl, nonce via `wp_localize_script`

## Reuse from Create Center

The hex-support plugin's Create Center already has: - Debounced AJAX search pattern - Multi-entity result grouping - Collapsible sections UI

Can adapt patterns from: - `class-create-center-search.php` → search logic - `create-center.js` → frontend patterns - `create-center.css` → section styling

---

*Forged on Day 240 — Left Hand awakens in Portal*