# Search & Indicators Behaviors

## Overview

The Search subsystem provides a global full-text search dialog accessible from the main navigation bar. It queries a Solr index of invoices, payments, transactions, and journal entries, returning results filtered by the user's client permissions. The Indicators subsystem provides small UI utilities like relative date badges (e.g., "5 days ago") used across the application.

**Testing Philosophy**
- Prefer unit tests for pure business logic (query parsing, date formatting, number formatting)
- Use integration tests for database interactions and cross-system flows
- Use UI tests only for end-to-end happy paths that touch multiple pages
- Every behavior must be user-visible; no tests for implementation details

---

## Testing Patterns

### Pattern: Search Query Parsing
Search queries are transformed before being sent to Solr:
1. Bare words become text search clauses joined with `AND`
2. Quoted phrases are preserved as exact tokens
3. Type keywords (`invoice`, `payment`, `transaction`, `journal-entry`) filter by document type
4. Dates in normal or ISO format are converted to Solr date format
5. Decimal numbers are formatted to 2 decimal places
6. Unparseable tokens pass through unchanged

**Test implications:** Unit test each transformation rule. Integration test the full query pipeline once.

### Pattern: Permission Filtering
All search results are filtered by the user's client access permissions.

**Test implications:** Integration test with users having different client access levels.

---

## Search

### Modal Behaviors

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 1.1 | It should open a search modal when the user clicks the search icon in the navbar | UI | [ ] |
| 1.2 | It should display an autofocused search input with placeholder text in the modal | UI | [ ] |
| 1.3 | It should trigger a search after 300ms debounce when the user types in the search input | Integration | [ ] |
| 1.4 | It should display the search modal without results when no query is provided | Integration | [ ] |

### Query Parsing Behaviors

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 2.1 | It should convert bare words in the search query to text search clauses joined with `AND` | Unit | [ ] |
| 2.2 | It should preserve quoted phrases in the search query as exact text matches | Unit | [ ] |
| 2.3 | It should filter by document type when the user includes a type keyword (`invoice`, `payment`, `transaction`, `journal-entry`) | Unit | [ ] |
| 2.4 | It should convert dates in normal format (e.g., `5/5/2034`) to Solr-compatible date format | Unit | [ ] |
| 2.5 | It should convert dates in ISO format to Solr-compatible date format | Unit | [ ] |
| 2.6 | It should pass through unparseable dates unchanged | Unit | [ ] |
| 2.7 | It should format decimal numbers to exactly 2 decimal places with `HALF_UP` rounding | Unit | [ ] |
| 2.8 | It should pass through integer numbers unchanged | Unit | [ ] |
| 2.9 | It should handle mixed type keywords and text tokens in the search query | Unit | [ ] |
| 2.10 | It should handle multiple type keywords in the search query | Unit | [ ] |

### Results Display Behaviors

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 3.1 | It should display search results as cards below the search input | UI | [ ] |
| 3.2 | It should show a type icon on each result card | UI | [ ] |
| 3.3 | It should show the document type name on each result card | UI | [ ] |
| 3.4 | It should show the client code as a pill on each result card | UI | [ ] |
| 3.5 | It should show the amount as a pill on each result card | UI | [ ] |
| 3.6 | It should show the vendor name as a pill when present on each result card | UI | [ ] |
| 3.7 | It should show the date on each result card | UI | [ ] |
| 3.8 | It should show the description or number on each result card | UI | [ ] |
| 3.9 | It should link each result card to the appropriate detail page with an `exact-match-id` parameter | Integration | [ ] |
| 3.10 | It should open the detail page in a new tab when the user clicks the external link icon | UI | [ ] |
| 3.11 | It should filter results to only show documents from clients the user can access | Integration | [ ] |

### Empty Results Behaviors

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 4.1 | It should display "No results found." when the query matches no documents | UI | [ ] |
| 4.2 | It should return an empty results list when the user has no accessible clients | Integration | [ ] |
| 4.3 | It should return an empty results list when Solr is unavailable | Integration | [ ] |

### Type Filter Behaviors

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 5.1 | It should filter results to only show payment documents when the user types "payment" | Integration | [ ] |
| 5.2 | It should filter results to only show invoice documents when the user types "invoice" | Integration | [ ] |
| 5.3 | It should show the payment icon and link to the payments detail page for payment results | UI | [ ] |

### Date Search Behaviors

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 6.1 | It should return documents matching a date in normal format (e.g., `5/5/2034`) | Integration | [ ] |
| 6.2 | It should return documents matching a date in ISO format | Integration | [ ] |
| 6.3 | It should accept future dates in the search query | Integration | [ ] |

---

## Indicators

### Days-Ago Behaviors

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 7.1 | It should display a relative date badge showing "N days ago" for past dates | UI | [ ] |
| 7.2 | It should display a relative date badge showing "N days from now" for future dates | UI | [ ] |
| 7.3 | It should show the days-ago badge in primary color for dates less than 30 days old | Unit + UI | [ ] |
| 7.4 | It should show the days-ago badge in secondary color for dates 30-59 days old | Unit + UI | [ ] |
| 7.5 | It should show the days-ago badge in yellow color for dates 60-89 days old | Unit + UI | [ ] |
| 7.6 | It should show the days-ago badge in red color for dates 90 or more days old | Unit + UI | [ ] |
| 7.7 | It should show "0 days ago" with primary color for same-day dates | Unit + UI | [ ] |
| 7.8 | It should render an empty indicator when the date is nil | Unit + UI | [ ] |

---

## Cross-Cutting Behaviors

### Permission Behaviors

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 8.1 | It should require authentication to access the search endpoint | Integration | [ ] |
| 8.2 | It should require authentication to access the days-ago endpoint | Integration | [ ] |
| 8.3 | It should redirect unauthenticated users to the login page when accessing search or days-ago | Integration | [ ] |

### Error Handling Behaviors

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 9.1 | It should reject invalid date formats for the days-ago endpoint via schema validation | Integration | [ ] |
| 9.2 | It should handle special characters in the search query without errors | Integration | [ ] |
| 9.3 | It should handle very long search queries without UI breakage | UI | [ ] |
| 9.4 | It should handle numeric tokens with commas or currency symbols as literal text search | Unit | [ ] |

---

## Test Data Requirements

| Entity | Requirements |
|--------|-------------|
| **Solr Index** | Indexed documents of all four types: `invoice`, `payment`, `transaction`, `journal-entry`; spanning multiple clients; with varying dates, amounts, descriptions, numbers, and vendor names |
| **Users** | Authenticated user with access to subset of clients; authenticated user with access to all clients; admin user |
| **Clients** | Multiple clients with `:client/code` and `:client/name` |
| **Invoices** | With `:invoice/invoice-number`, `:invoice/total`, `:invoice/date`, `:invoice/client`, `:invoice/vendor` |
| **Payments** | With `:payment/check-number`, `:payment/amount`, `:payment/date`, `:payment/client`, `:payment/vendor` |
| **Transactions** | With `:transaction/description-original`, `:transaction/amount`, `:transaction/date`, `:transaction/client`, `:transaction/vendor` |
| **Journal Entries** | With `:journal-entry/amount`, `:journal-entry/date`, `:journal-entry/client`, `:journal-entry/vendor`, `:journal-entry/line-items` |

## Existing Tests to Preserve

No existing test files specified for Search & Indicators.

## Dependencies

- **Solr**: Full-text search index (`auto-ap.solr`). Uses `MockSolrClient` in test environments without Solr configured
- **Datomic**: Client visibility checks pull user/client associations
- **HTMX**: Modal loading, search debounce (`keyup changed delay:300ms`), indicator spinner
- **Alpine.js**: Modal card structure
- **Middleware**: `wrap-secure` requires authentication; `wrap-client-redirect-unauthenticated` redirects unauthenticated to `/login`; `wrap-schema-enforce` validates `date` query parameter for `/days-ago`
- **Invoices**: Search results link to invoice detail pages
- **Payments**: Search results link to payment detail pages
- **Transactions**: Search results link to transaction detail pages
- **Ledger**: Search results link to ledger for journal entries
