@indxsearch/intrface 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,471 @@
1
+ # @indxsearch/intrface
2
+
3
+ A powerful, flexible React search UI library for INDX Search API.
4
+
5
+ ## Features
6
+
7
+ - 🔍 **Full-text search** with fuzzy matching and typo tolerance
8
+ - 🎯 **Faceted filtering** - Value filters (exact match) and range filters (numeric)
9
+ - 📊 **Real-time facet counts** - Dynamic aggregations that update with search
10
+ - 📱 **Mobile-responsive** - Built-in responsive design
11
+ - ⚡ **Debounced searches** - Optimized performance
12
+ - 🎨 **Customizable rendering** - Full control over result display
13
+ - 🔒 **Secure authentication** - Session-based authentication with automatic login
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @indxsearch/intrface @indxsearch/systm @indxsearch/pixl
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ### 1. Set Up Environment Variables
24
+
25
+ Create a `.env.local` file in your project root with your INDX credentials:
26
+
27
+ ```bash
28
+ # INDX Server Configuration
29
+ NEXT_PUBLIC_INDX_URL=https://your-indx-server.com
30
+
31
+ # Authentication Credentials
32
+ NEXT_PUBLIC_INDX_EMAIL=your@email.com
33
+ NEXT_PUBLIC_INDX_PASSWORD=yourpassword
34
+ ```
35
+
36
+ **For local development:**
37
+ ```bash
38
+ NEXT_PUBLIC_INDX_URL=http://localhost:38171
39
+ NEXT_PUBLIC_INDX_EMAIL=your@email.com
40
+ NEXT_PUBLIC_INDX_PASSWORD=yourpassword
41
+ ```
42
+
43
+ **Security Notes:**
44
+ - Never commit `.env.local` to version control
45
+ - Store credentials securely in environment variables
46
+ - The library automatically calls the Login API on initialization to get a fresh session token
47
+ - Session tokens are managed internally and refreshed as needed
48
+
49
+ ### 2. Import Styles
50
+
51
+ Import the CSS file in your app entry point:
52
+
53
+ ```typescript
54
+ import '@indxsearch/intrface/styles.css';
55
+ ```
56
+
57
+ ### 3. Basic Implementation
58
+
59
+ ```typescript
60
+ 'use client';
61
+ import { SearchProvider, SearchInput, SearchResults } from '@indxsearch/intrface';
62
+
63
+ export default function SearchPage() {
64
+ return (
65
+ <SearchProvider
66
+ url={process.env.NEXT_PUBLIC_INDX_URL!}
67
+ email={process.env.NEXT_PUBLIC_INDX_EMAIL!}
68
+ password={process.env.NEXT_PUBLIC_INDX_PASSWORD!}
69
+ dataset="products"
70
+ >
71
+ <SearchInput placeholder="Search products..." />
72
+
73
+ <SearchResults
74
+ fields={['name', 'description', 'category']}
75
+ resultsPerPage={10}
76
+ >
77
+ {(item) => (
78
+ <div>
79
+ <h3>{item.name}</h3>
80
+ <p>{item.description}</p>
81
+ </div>
82
+ )}
83
+ </SearchResults>
84
+ </SearchProvider>
85
+ );
86
+ }
87
+ ```
88
+
89
+ **Using different datasets on different pages:**
90
+
91
+ ```typescript
92
+ // products page
93
+ <SearchProvider url={url} email={email} password={password} dataset="products">
94
+ {/* ... */}
95
+ </SearchProvider>
96
+
97
+ // articles page
98
+ <SearchProvider url={url} email={email} password={password} dataset="articles">
99
+ {/* ... */}
100
+ </SearchProvider>
101
+ ```
102
+
103
+ ## Authentication
104
+
105
+ The library uses **session-based authentication** that automatically logs in when the app initializes.
106
+
107
+ ### How It Works
108
+
109
+ 1. You provide your email and password to `SearchProvider`
110
+ 2. On mount, the library automatically calls the Login API endpoint
111
+ 3. A fresh session token is obtained and used for all subsequent requests
112
+ 4. No need to manually manage tokens - it's all handled internally
113
+
114
+ ```typescript
115
+ <SearchProvider
116
+ url="https://your-indx-server.com"
117
+ email="your@email.com"
118
+ password="yourpassword"
119
+ dataset="products"
120
+ >
121
+ {/* Your search UI */}
122
+ </SearchProvider>
123
+ ```
124
+
125
+ **Authentication Benefits:**
126
+ - ✅ Automatic login on app initialization
127
+ - ✅ Fresh session tokens on every app load
128
+ - ✅ No manual token management required
129
+ - ✅ Works reliably after server restarts
130
+
131
+ **Security Best Practices:**
132
+ - Store credentials in environment variables (`.env.local`)
133
+ - Never commit credentials to version control
134
+ - Use secure HTTPS connections in production
135
+
136
+ ## Error Handling
137
+
138
+ The library includes comprehensive error handling with helpful console messages:
139
+
140
+ ### Automatic Error Detection
141
+
142
+ The SearchProvider automatically validates:
143
+ - ✅ Authentication credentials (email/password)
144
+ - ✅ Login success and token retrieval
145
+ - ✅ Dataset existence and status
146
+ - ✅ Dataset readiness (indexing complete)
147
+ - ✅ Empty dataset warnings
148
+ - ✅ Network connectivity
149
+
150
+ All errors include:
151
+ - Clear error messages
152
+ - Specific problem identification
153
+ - Actionable fix suggestions
154
+ - Example commands to resolve issues
155
+
156
+ ### Error Boundary (Optional)
157
+
158
+ Wrap your search interface with `SearchErrorBoundary` for graceful error handling:
159
+
160
+ ```typescript
161
+ import { SearchErrorBoundary, SearchProvider } from '@indxsearch/intrface';
162
+
163
+ <SearchErrorBoundary>
164
+ <SearchProvider url={url} email={email} password={password} dataset={dataset}>
165
+ {/* Your search UI */}
166
+ </SearchProvider>
167
+ </SearchErrorBoundary>
168
+ ```
169
+
170
+ **Custom error UI:**
171
+ ```typescript
172
+ <SearchErrorBoundary
173
+ fallback={(error, reset) => (
174
+ <div>
175
+ <h2>Search Error</h2>
176
+ <p>{error.message}</p>
177
+ <button onClick={reset}>Try Again</button>
178
+ </div>
179
+ )}
180
+ >
181
+ <SearchProvider url={url} email={email} password={password} dataset={dataset}>
182
+ {children}
183
+ </SearchProvider>
184
+ </SearchErrorBoundary>
185
+ ```
186
+
187
+ ### Console Error Messages
188
+
189
+ All errors show in the browser console with emoji indicators:
190
+ - ✅ = Success
191
+ - 🔍 = Checking something
192
+ - ⚠️ = Warning (non-critical)
193
+ - ❌ = Error (needs fixing)
194
+ - 💡 = Helpful suggestion
195
+
196
+ **Example:**
197
+ ```
198
+ [Auth] ❌ Dataset "products" not found (404)
199
+ [Auth] 💡 Available datasets can be checked with: curl -X GET ...
200
+ [Auth] 💡 Make sure you spelled the dataset name correctly
201
+ ```
202
+
203
+ ## Adding Filters
204
+
205
+ ### Value Filters (Exact Match)
206
+
207
+ ```typescript
208
+ import { ValueFilterPanel } from '@indxsearch/intrface';
209
+
210
+ <SearchProvider {...authProps}>
211
+ <SearchInput />
212
+
213
+ {/* Simple checkbox list */}
214
+ <ValueFilterPanel
215
+ field="category"
216
+ label="Category"
217
+ />
218
+
219
+ {/* Button-style filters */}
220
+ <ValueFilterPanel
221
+ field="brand"
222
+ label="Brand"
223
+ displayType="button"
224
+ layout="grid"
225
+ />
226
+
227
+ <SearchResults {...resultsProps}>
228
+ {renderItem}
229
+ </SearchResults>
230
+ </SearchProvider>
231
+ ```
232
+
233
+ ### Range Filters (Numeric)
234
+
235
+ ```typescript
236
+ import { RangeFilterPanel } from '@indxsearch/intrface';
237
+
238
+ <RangeFilterPanel
239
+ field="price"
240
+ label="Price Range"
241
+ min={0}
242
+ max={1000}
243
+ />
244
+ ```
245
+
246
+ ### Active Filters Display
247
+
248
+ ```typescript
249
+ import { ActiveFiltersPanel } from '@indxsearch/intrface';
250
+
251
+ <ActiveFiltersPanel />
252
+ ```
253
+
254
+ ## Full Example with Filters
255
+
256
+ ```typescript
257
+ 'use client';
258
+ import {
259
+ SearchProvider,
260
+ SearchInput,
261
+ SearchResults,
262
+ ValueFilterPanel,
263
+ RangeFilterPanel,
264
+ ActiveFiltersPanel,
265
+ SortByPanel,
266
+ } from '@indxsearch/intrface';
267
+
268
+ export default function AdvancedSearch() {
269
+ return (
270
+ <SearchProvider
271
+ url={process.env.NEXT_PUBLIC_INDX_URL!}
272
+ email={process.env.NEXT_PUBLIC_INDX_EMAIL!}
273
+ password={process.env.NEXT_PUBLIC_INDX_PASSWORD!}
274
+ dataset="products"
275
+ allowEmptySearch={true}
276
+ enableFacets={true}
277
+ maxResults={20}
278
+ >
279
+ <div style={{ display: 'flex', gap: '2rem' }}>
280
+ {/* Sidebar with filters */}
281
+ <aside style={{ width: '250px' }}>
282
+ <ActiveFiltersPanel />
283
+ <SortByPanel displayType="radio" />
284
+ <ValueFilterPanel field="category" label="Category" />
285
+ <ValueFilterPanel field="brand" label="Brand" displayType="button" />
286
+ <RangeFilterPanel field="price" label="Price" />
287
+ </aside>
288
+
289
+ {/* Main content */}
290
+ <main style={{ flex: 1 }}>
291
+ <SearchInput placeholder="Search products..." showFocus={true} />
292
+
293
+ <SearchResults
294
+ fields={['name', 'description', 'price', 'category', 'brand']}
295
+ resultsPerPage={20}
296
+ >
297
+ {(item) => (
298
+ <div style={{ padding: '1rem', borderBottom: '1px solid #eee' }}>
299
+ <h3>{item.name}</h3>
300
+ <p>{item.description}</p>
301
+ <div>
302
+ <strong>${item.price}</strong> • {item.category}
303
+ </div>
304
+ </div>
305
+ )}
306
+ </SearchResults>
307
+ </main>
308
+ </div>
309
+ </SearchProvider>
310
+ );
311
+ }
312
+ ```
313
+
314
+ ## API Reference
315
+
316
+ ### SearchProvider Props
317
+
318
+ | Prop | Type | Required | Default | Description |
319
+ |------|------|----------|---------|-------------|
320
+ | `url` | `string` | ✅ | - | INDX server URL |
321
+ | `email` | `string` | ✅ | - | User email for authentication |
322
+ | `password` | `string` | ✅ | - | User password for authentication |
323
+ | `dataset` | `string` | ✅ | - | Dataset name |
324
+ | `allowEmptySearch` | `boolean` | ❌ | `false` | Show results without query |
325
+ | `enableFacets` | `boolean` | ❌ | `true` | Enable faceted search |
326
+ | `maxResults` | `number` | ❌ | `10` | Max results per search |
327
+ | `facetDebounceDelayMillis` | `number` | ❌ | `500` | Debounce delay for facet updates |
328
+ | `coverageDepth` | `number` | ❌ | `500` | Search depth for fuzzy matching |
329
+ | `removeDuplicates` | `boolean` | ❌ | `false` | Remove duplicate results |
330
+
331
+ ### SearchInput Props
332
+
333
+ | Prop | Type | Default | Description |
334
+ |------|------|---------|-------------|
335
+ | `placeholder` | `string` | `'Search...'` | Input placeholder text |
336
+ | `showClear` | `boolean` | `true` | Show clear button |
337
+ | `showFocus` | `boolean` | `false` | Show focus ring |
338
+ | `inputSize` | `'small' \| 'default' \| 'large'` | `'default'` | Input size |
339
+
340
+ ### SearchResults Props
341
+
342
+ | Prop | Type | Required | Description |
343
+ |------|------|----------|-------------|
344
+ | `fields` | `string[]` | ✅ | Document fields to fetch |
345
+ | `resultsPerPage` | `number` | ✅ | Results per page |
346
+ | `children` | `(item: any) => ReactNode` | ✅ | Render function for each result |
347
+
348
+ ### ValueFilterPanel Props
349
+
350
+ | Prop | Type | Default | Description |
351
+ |------|------|---------|-------------|
352
+ | `field` | `string` | ✅ | Field name to filter on |
353
+ | `label` | `string` | ❌ | Display label |
354
+ | `displayType` | `'checkbox' \| 'button'` | `'checkbox'` | Filter UI style |
355
+ | `layout` | `'list' \| 'grid'` | `'list'` | Layout style |
356
+ | `limit` | `number` | `undefined` | Max filters to show |
357
+ | `startCollapsed` | `boolean` | `false` | Start collapsed |
358
+ | `showCount` | `boolean` | `true` | Show facet counts |
359
+
360
+ ### RangeFilterPanel Props
361
+
362
+ | Prop | Type | Required | Description |
363
+ |------|------|----------|-------------|
364
+ | `field` | `string` | ✅ | Field name to filter on |
365
+ | `label` | `string` | ❌ | Display label |
366
+ | `min` | `number` | ❌ | Minimum value |
367
+ | `max` | `number` | ❌ | Maximum value |
368
+
369
+ ## Troubleshooting
370
+
371
+ ### "Login failed" error
372
+
373
+ **Problem:** Authentication credentials are invalid
374
+
375
+ **Solutions:**
376
+ 1. Verify your email and password are correct
377
+ 2. Check that the credentials match your INDX account
378
+ 3. Ensure the INDX server URL is correct
379
+ 4. Check browser console for detailed error messages
380
+
381
+ ### "401 Unauthorized" errors after successful login
382
+
383
+ **Problem:** Session token became invalid
384
+
385
+ **Solutions:**
386
+ 1. Refresh the page to get a new session token (automatic login)
387
+ 2. Verify the server is running and accessible
388
+ 3. Check server logs for authentication issues
389
+
390
+ ### "Failed to fetch" errors
391
+
392
+ **Problem:** Cannot connect to INDX server
393
+
394
+ **Solutions:**
395
+ 1. Verify the server URL is correct
396
+ 2. Check if the server is running (for local: `http://localhost:38171`)
397
+ 3. Ensure CORS is configured on the server
398
+ 4. Check browser console for detailed error
399
+
400
+ ### Results not showing
401
+
402
+ **Problem:** Empty results even with data
403
+
404
+ **Solutions:**
405
+ 1. Verify dataset name is correct
406
+ 2. Check if dataset is indexed (use GetStatus endpoint)
407
+ 3. Ensure fields are configured as indexable/facetable
408
+ 4. Try `allowEmptySearch={true}` to see all results
409
+
410
+ ### Filters not working
411
+
412
+ **Problem:** Filters don't update results
413
+
414
+ **Solutions:**
415
+ 1. Ensure fields are configured as filterable/facetable in your dataset
416
+ 2. Check browser console for errors
417
+ 3. Verify field names match your dataset
418
+
419
+ ## Examples
420
+
421
+ ### Example 1: E-commerce Search
422
+
423
+ ```typescript
424
+ <SearchProvider url={url} email={email} password={password} dataset="products">
425
+ <div className="search-page">
426
+ <SearchInput placeholder="Search products..." />
427
+
428
+ <div className="filters">
429
+ <ValueFilterPanel field="category" label="Category" />
430
+ <ValueFilterPanel field="brand" label="Brand" displayType="button" />
431
+ <RangeFilterPanel field="price" label="Price" min={0} max={1000} />
432
+ <ValueFilterPanel field="inStock" label="In Stock" />
433
+ </div>
434
+
435
+ <SearchResults fields={['name', 'price', 'image']} resultsPerPage={24}>
436
+ {(product) => (
437
+ <ProductCard
438
+ name={product.name}
439
+ price={product.price}
440
+ image={product.image}
441
+ />
442
+ )}
443
+ </SearchResults>
444
+ </div>
445
+ </SearchProvider>
446
+ ```
447
+
448
+ ### Example 2: Document Search
449
+
450
+ ```typescript
451
+ <SearchProvider url={url} email={email} password={password} dataset="documents">
452
+ <SearchInput placeholder="Search documents..." />
453
+
454
+ <ValueFilterPanel field="docType" label="Type" />
455
+ <ValueFilterPanel field="author" label="Author" />
456
+
457
+ <SearchResults fields={['title', 'content', 'date']} resultsPerPage={10}>
458
+ {(doc) => (
459
+ <article>
460
+ <h2>{doc.title}</h2>
461
+ <p>{doc.content.substring(0, 200)}...</p>
462
+ <small>{new Date(doc.date).toLocaleDateString()}</small>
463
+ </article>
464
+ )}
465
+ </SearchResults>
466
+ </SearchProvider>
467
+ ```
468
+
469
+ ## Support
470
+
471
+ - **Documentation:** [docs.indx.co](https://docs.indx.co)