@dashadmin/dash-components 1.3.25 → 1.3.28

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.
Files changed (112) hide show
  1. package/README.md +35 -0
  2. package/dist/components/AuditLog/AuditLog.js +345 -1
  3. package/dist/components/AuditLog/index.js +4 -1
  4. package/dist/components/AutoCompleteArrayInput/AutoCompleteCheckBoxInput.js +630 -3
  5. package/dist/components/AutoCompleteArrayInput/AutocompleteCheckBoxArrayInput.js +53 -1
  6. package/dist/components/AutoCompleteArrayInputAutoSearch/AutoCompleteCheckBoxInput.js +647 -3
  7. package/dist/components/AutoCompleteArrayInputAutoSearch/AutocompleteCheckBoxArrayInput.js +53 -1
  8. package/dist/components/Json/Json.js +235 -1
  9. package/dist/components/JsonColorSelector/JsonColorSelector.js +1172 -1
  10. package/dist/components/JsonColorSelector/JsonColorSelectorEnhanced.js +863 -2
  11. package/dist/components/JsonColorSelector/JsonCssVarValues.js +738 -1
  12. package/dist/components/JsonColorSelector/components/ColorEditDialog.js +426 -1
  13. package/dist/components/JsonColorSelector/components/ColorPaletteItem.js +75 -1
  14. package/dist/components/JsonColorSelector/components/ImageColorExtractor.js +283 -1
  15. package/dist/components/JsonColorSelector/helpers/functions.js +101 -1
  16. package/dist/components/JsonColorSelector/useAsyncColorThief.js +105 -1
  17. package/dist/components/ListActive/ListActive.js +126 -1
  18. package/dist/components/ListActive/ListBoolean.js +124 -1
  19. package/dist/components/NotificationPreferences/NotificationPreferences.js +317 -1
  20. package/dist/components/NotificationPreferences/index.js +7 -1
  21. package/dist/components/SearchableSelects/RASearchableSelect.js +236 -1
  22. package/dist/components/SearchableSelects/RASearchableSelectCheckboxes.js +479 -1
  23. package/dist/components/SearchableSelects/RASearchableSelectChips.js +465 -1
  24. package/dist/components/SearchableSelects/RASearchableSelectChipsRecordContext.js +295 -1
  25. package/dist/components/SearchableSelects/SearchableSelect.js +202 -1
  26. package/dist/components/SearchableSelects/SearchableSelectCheckboxes.js +290 -1
  27. package/dist/components/SortableDataGrid/SortableDataGrid.js +72 -1
  28. package/dist/components/SortableDataGrid/SortableDataGridBody.js +167 -1
  29. package/dist/components/SortableDataGrid/SortableDataGridHeader.js +109 -1
  30. package/dist/components/SortableDataGrid/SortableDataGridRow.js +283 -1
  31. package/dist/components/Upload/SingleImageUploader.js +40 -1
  32. package/dist/components/custom/PackageCopyMethod.js +78 -1
  33. package/dist/components/custom/PackageRadioButtonGroup.js +45 -1
  34. package/dist/components/dialog/Basic.js +8 -1
  35. package/dist/components/dialog/Correct.js +14 -1
  36. package/dist/components/dialog/Error.js +14 -1
  37. package/dist/components/dialog/Info.js +14 -1
  38. package/dist/components/dialog/InfoV2.js +21 -1
  39. package/dist/components/dialog/NotFound.js +14 -1
  40. package/dist/components/dialog/QuickSearch.js +117 -1
  41. package/dist/components/notifications/NotificationsCenter.js +597 -1
  42. package/dist/components/notifications/index.js +24 -1
  43. package/dist/components/tables/PaginationComponent.js +36 -1
  44. package/dist/components/theme/AppLoadingFallback.js +38 -2
  45. package/dist/components/theme/ErrorBoundary.js +52 -1
  46. package/dist/components/theme/InitialLoader.js +13 -1
  47. package/dist/components/theme/NotFound.js +136 -1
  48. package/dist/components/theme/NotResults.js +38 -1
  49. package/dist/hooks/useDraggable.js +6 -1
  50. package/dist/hooks/useNotifications.js +8 -1
  51. package/dist/hooks/useQuickSearch.js +26 -1
  52. package/dist/index.js +66 -5567
  53. package/dist/utils/setNativeValue.js +17 -1
  54. package/package.json +148 -132
  55. package/src/components/AuditLog/AuditLog.tsx +476 -0
  56. package/src/components/AuditLog/index.ts +2 -0
  57. package/src/components/AutoCompleteArrayInput/AutoCompleteCheckBoxInput.tsx +917 -0
  58. package/src/components/AutoCompleteArrayInput/AutocompleteCheckBoxArrayInput.tsx +39 -0
  59. package/src/components/AutoCompleteArrayInputAutoSearch/AutoCompleteCheckBoxInput.tsx +916 -0
  60. package/src/components/AutoCompleteArrayInputAutoSearch/AutocompleteCheckBoxArrayInput.tsx +44 -0
  61. package/src/components/Json/Json.tsx +312 -0
  62. package/src/components/JsonColorSelector/JsonColorSelector.tsx +1435 -0
  63. package/src/components/JsonColorSelector/JsonColorSelectorEnhanced.tsx +1029 -0
  64. package/src/components/JsonColorSelector/JsonCssVarValues.tsx +863 -0
  65. package/src/components/JsonColorSelector/components/ColorEditDialog.tsx +491 -0
  66. package/src/components/JsonColorSelector/components/ColorPaletteItem.tsx +80 -0
  67. package/src/components/JsonColorSelector/components/ImageColorExtractor.tsx +328 -0
  68. package/src/components/JsonColorSelector/helpers/functions.tsx +123 -0
  69. package/src/components/JsonColorSelector/interfaces/interfaces.tsx +22 -0
  70. package/src/components/JsonColorSelector/useAsyncColorThief.tsx +97 -0
  71. package/src/components/ListActive/ListActive.tsx +106 -0
  72. package/src/components/ListActive/ListBoolean.tsx +96 -0
  73. package/src/components/NotificationPreferences/NotificationPreferences.tsx +463 -0
  74. package/src/components/NotificationPreferences/index.tsx +2 -0
  75. package/src/components/SearchableSelects/RASearchableSelect.tsx +300 -0
  76. package/src/components/SearchableSelects/RASearchableSelectCheckboxes.tsx +604 -0
  77. package/src/components/SearchableSelects/RASearchableSelectChips.tsx +558 -0
  78. package/src/components/SearchableSelects/RASearchableSelectChipsRecordContext.tsx +362 -0
  79. package/src/components/SearchableSelects/SearchableSelect.tsx +321 -0
  80. package/src/components/SearchableSelects/SearchableSelectCheckboxes.tsx +355 -0
  81. package/src/components/SortableDataGrid/SortableDataGrid.tsx +56 -0
  82. package/src/components/SortableDataGrid/SortableDataGridBody.tsx +175 -0
  83. package/src/components/SortableDataGrid/SortableDataGridHeader.tsx +171 -0
  84. package/src/components/SortableDataGrid/SortableDataGridRow.tsx +247 -0
  85. package/src/components/Upload/SingleImageUploader.tsx +55 -0
  86. package/src/components/custom/PackageCopyMethod.tsx +109 -0
  87. package/src/components/custom/PackageRadioButtonGroup.tsx +47 -0
  88. package/src/components/dialog/Basic.tsx +11 -0
  89. package/src/components/dialog/Correct.tsx +16 -0
  90. package/src/components/dialog/Error.tsx +15 -0
  91. package/src/components/dialog/Info.tsx +15 -0
  92. package/src/components/dialog/InfoV2.tsx +35 -0
  93. package/src/components/dialog/NotFound.tsx +18 -0
  94. package/src/components/dialog/QuickSearch.tsx +115 -0
  95. package/src/components/notifications/NotificationsCenter.tsx +792 -0
  96. package/src/components/notifications/index.ts +42 -0
  97. package/src/components/tables/PaginationComponent.tsx +17 -0
  98. package/src/components/theme/AppLoadingFallback.tsx +42 -0
  99. package/src/components/theme/ErrorBoundary.tsx +57 -0
  100. package/src/components/theme/InitialLoader.tsx +16 -0
  101. package/src/components/theme/NotFound.tsx +124 -0
  102. package/src/components/theme/NotResults.tsx +54 -0
  103. package/src/hooks/useDraggable.ts +11 -0
  104. package/src/hooks/useNotifications.ts +14 -0
  105. package/src/hooks/useQuickSearch.tsx +29 -0
  106. package/src/index.tsx +77 -0
  107. package/src/utils/setNativeValue.tsx +23 -0
  108. package/dist/color-thief-CTwGFhOB.js +0 -272
  109. /package/{dist → src}/components/AutoCompleteArrayInputAutoSearch/AutocompleteCheckBoxArrayAutoSearch.tsx.wip +0 -0
  110. /package/{dist → src}/components/JsonColorSelector/JsonColorSelector.tsx.bkup +0 -0
  111. /package/{dist → src}/components/SortableDataGrid/README.md +0 -0
  112. /package/{dist → src}/styles/index.less +0 -0
@@ -0,0 +1,558 @@
1
+ /**
2
+ * Standalone SearchableSelectChips for filters and other uses
3
+ * @author Francisco Aranda <francisco.aranda@sudo.cl> <faranda@gmail.com>
4
+ */
5
+
6
+ import {
7
+ Autocomplete,
8
+ Box,
9
+ Chip,
10
+ CircularProgress,
11
+ TextField,
12
+ } from "@mui/material";
13
+ import React, { useEffect, useState } from "react";
14
+ import { useGetList } from "react-admin";
15
+ import { isArray } from "lodash";
16
+ import { LoadingIndicator } from "react-admin";
17
+
18
+ interface ISearchableSelectChips {
19
+ name?: string;
20
+ resource?: string;
21
+ selectLabel?: string;
22
+ title?: string;
23
+ renderText: (option: any, caller: string) => any;
24
+ transformData?: (val: any) => any;
25
+ defaultValues?: any;
26
+ value?: any;
27
+ onChange?: (value: any) => void;
28
+ isMultiple?: boolean;
29
+ minSearch?: number;
30
+ searchResults?: number;
31
+ minOptions?: number; // Minimum options to show
32
+ timerSearch?: number;
33
+ isEmpty?: boolean;
34
+ filter?: { [x: string]: any };
35
+ queryFilter?: string;
36
+ isOptionEqualToValue: (option: any, value: any) => boolean;
37
+ viewAttribute: string;
38
+ valueKeyId?: string;
39
+ transformOption?: (
40
+ option: any,
41
+ parsedOptions: any[],
42
+ caller?: string
43
+ ) => boolean;
44
+ disabled?: boolean;
45
+ placeholder?: string;
46
+ emptyText?: string;
47
+ fullWidth?: boolean;
48
+ // 🔥 NEW: Enable mixed results feature
49
+ enableMixedResults?: boolean;
50
+ }
51
+
52
+ export const SearchableSelectChips: React.FC<ISearchableSelectChips> = ({
53
+ isMultiple = false,
54
+ searchResults = 50,
55
+ minSearch = 2,
56
+ minOptions = 10,
57
+ timerSearch = 500,
58
+ resource,
59
+ selectLabel,
60
+ renderText,
61
+ transformData = (val: any) => val,
62
+ queryFilter = "q",
63
+ defaultValues = undefined,
64
+ value,
65
+ onChange,
66
+ isEmpty = false,
67
+ filter,
68
+ isOptionEqualToValue,
69
+ viewAttribute = null,
70
+ valueKeyId = "id",
71
+ transformOption,
72
+ disabled = false,
73
+ placeholder = "",
74
+ emptyText = "",
75
+ fullWidth = true,
76
+ // 🔥 NEW: Mixed results props
77
+ enableMixedResults = false,
78
+ }) => {
79
+ // Component state
80
+ const [open, setOpen] = useState(false);
81
+ const [fullyLoaded, setFullyLoaded] = useState(false);
82
+ const [parsedOptions, setParsedOptions] = useState<any[]>([]);
83
+ const [q, setQ] = useState("");
84
+ const [selectedOptions, setSelectedOptions] = useState<any[]>([]);
85
+ const [timeOutSearch, setTimeOutSearch] = useState<any>(undefined);
86
+
87
+ const _transformOption = (option: any, caller: string) => {
88
+ if (typeof transformOption === "function") {
89
+ return transformOption(option, parsedOptions, caller);
90
+ }
91
+ return option;
92
+ };
93
+
94
+ // Get includes for the API call
95
+ const getIncludes = () => {
96
+ const currentValue = value || defaultValues;
97
+ if (!currentValue) return [];
98
+
99
+ if (Array.isArray(currentValue)) {
100
+ return currentValue.map(item =>
101
+ typeof item === 'object' ? item[valueKeyId] : item
102
+ ).filter(Boolean);
103
+ }
104
+
105
+ const id = typeof currentValue === 'object' ? currentValue[valueKeyId] : currentValue;
106
+ return id ? [id] : [];
107
+ };
108
+
109
+ const includes = getIncludes();
110
+
111
+ const buildFilterParams = () => {
112
+ const baseFilter = { ...filter };
113
+
114
+ // Add includes if we have selected values
115
+ if (includes.length > 0) {
116
+ baseFilter.includes = includes;
117
+ }
118
+
119
+ console.log('🔧 SearchableSelectChips DEBUG - buildFilterParams called:', {
120
+ resource,
121
+ enableMixedResults,
122
+ searchQuery: q,
123
+ hasSearch: q && q.length >= minSearch,
124
+ timestamp: new Date().toISOString()
125
+ });
126
+
127
+ // 🔥 NEW: Mixed results logic with query parameter
128
+ if (enableMixedResults) {
129
+ const mixedResultsValue = minOptions;
130
+
131
+ // Always add mixedResults parameter when enabled
132
+ baseFilter.mixedResults = mixedResultsValue;
133
+
134
+ console.log('🔥 SearchableSelectChips DEBUG - Mixed results enabled via query param:', {
135
+ resource,
136
+ mixedResults: mixedResultsValue,
137
+ searchQuery: q,
138
+ hasSearch: q && q.length >= minSearch,
139
+ baseFilter,
140
+ timestamp: new Date().toISOString()
141
+ });
142
+
143
+ // Add search query if present
144
+ if (q && q.length >= minSearch) {
145
+ baseFilter[queryFilter] = q;
146
+ }
147
+
148
+ // Set limit
149
+ baseFilter.limit = searchResults;
150
+
151
+ } else {
152
+ // 🔥 FALLBACK: Original logic for backward compatibility
153
+ if (q && q.length >= minSearch) {
154
+ baseFilter[queryFilter] = q;
155
+ baseFilter.mixed_results = true;
156
+ baseFilter.min_options = minOptions;
157
+ baseFilter.limit = searchResults;
158
+
159
+ console.log('🔍 SearchableSelectChips DEBUG - Legacy mixed results enabled:', {
160
+ resource,
161
+ searchQuery: q,
162
+ minOptions,
163
+ searchResults,
164
+ mixed_results: true,
165
+ baseFilter,
166
+ timestamp: new Date().toISOString()
167
+ });
168
+ } else if (q === "") {
169
+ baseFilter.limit = Math.max(minOptions, searchResults);
170
+ } else {
171
+ baseFilter.limit = Math.max(minOptions, searchResults);
172
+ }
173
+ }
174
+
175
+ console.log('🚀 SearchableSelectChips DEBUG - Final filter params:', {
176
+ resource,
177
+ filterParams: baseFilter,
178
+ enableMixedResults,
179
+ timestamp: new Date().toISOString()
180
+ });
181
+
182
+ return baseFilter;
183
+ };
184
+
185
+ const filterParams = buildFilterParams();
186
+
187
+ // React Admin useGetList with dynamic filter
188
+ const {
189
+ data: resourceSearchResults,
190
+ isLoading: isResourceSearchLoading,
191
+ error: isResourceSearchErrored,
192
+ } = useGetList(resource, {
193
+ meta: { removeSortFilters: true },
194
+ filter: filterParams,
195
+ });
196
+
197
+ // Update parsed options when search results change
198
+ useEffect(() => {
199
+ if (resourceSearchResults) {
200
+ const resultsArray = Array.isArray(resourceSearchResults)
201
+ ? resourceSearchResults
202
+ : Object.values(resourceSearchResults);
203
+
204
+ console.log('📊 SearchableSelectChips DEBUG - Processing results:', {
205
+ resource,
206
+ totalResults: resultsArray.length,
207
+ searchQuery: q,
208
+ hasSearch: q && q.length >= minSearch,
209
+ enableMixedResults,
210
+ timestamp: new Date().toISOString()
211
+ });
212
+
213
+ let processedOptions = resultsArray.map((ele, index) => {
214
+ // Determine if this is a search match (client-side check for visual indicators)
215
+ const isSearchMatch = q && q.length >= minSearch ?
216
+ (ele[viewAttribute] && ele[viewAttribute].toLowerCase().includes(q.toLowerCase())) :
217
+ true;
218
+
219
+ return {
220
+ ...ele,
221
+ key: `option-${ele[valueKeyId] || index}`,
222
+ isSearchMatch,
223
+ priority: isSearchMatch ? 1 : 2,
224
+ };
225
+ });
226
+
227
+ // Sort by priority (search matches first) and then by name
228
+ processedOptions.sort((a, b) => {
229
+ if (a.priority !== b.priority) {
230
+ return a.priority - b.priority;
231
+ }
232
+ const nameA = a[viewAttribute] || a.name || '';
233
+ const nameB = b[viewAttribute] || b.name || '';
234
+ return nameA.localeCompare(nameB);
235
+ });
236
+
237
+ // Count search matches vs non-matches
238
+ const searchMatches = processedOptions.filter(opt => opt.isSearchMatch);
239
+ const nonMatches = processedOptions.filter(opt => !opt.isSearchMatch);
240
+
241
+ console.log('🎯 SearchableSelectChips DEBUG - Results breakdown:', {
242
+ resource,
243
+ searchQuery: q,
244
+ totalProcessed: processedOptions.length,
245
+ searchMatches: searchMatches.length,
246
+ nonMatches: nonMatches.length,
247
+ enableMixedResults,
248
+ mixedResultsParam: enableMixedResults ? (minOptions) : 'disabled',
249
+ timestamp: new Date().toISOString()
250
+ });
251
+
252
+ // Limit results
253
+ if (processedOptions.length > searchResults) {
254
+ processedOptions = processedOptions.slice(0, searchResults);
255
+ }
256
+
257
+ setParsedOptions(processedOptions);
258
+ }
259
+ }, [resourceSearchResults, valueKeyId, q, minSearch, viewAttribute, searchResults, minOptions, enableMixedResults]);
260
+
261
+ useEffect(() => {
262
+ console.log('🔍 SearchableSelectChips DEBUG - Component initialized with resource:', {
263
+ resource,
264
+ resourceType: typeof resource,
265
+ selectLabel,
266
+ isMultiple,
267
+ minOptions,
268
+ searchResults,
269
+ minSearch,
270
+ timestamp: new Date().toISOString()
271
+ });
272
+ }, []);
273
+
274
+ // Initialize selected options from value or defaultValues
275
+ useEffect(() => {
276
+ const currentValue = value !== undefined ? value : defaultValues;
277
+
278
+ console.log('🎯 SearchableSelectChips DEBUG - Value changed:', {
279
+ resource,
280
+ currentValue,
281
+ valueType: typeof currentValue,
282
+ isArray: Array.isArray(currentValue),
283
+ isMultiple,
284
+ timestamp: new Date().toISOString()
285
+ });
286
+
287
+ if (currentValue === null || currentValue === undefined) {
288
+ setSelectedOptions([]);
289
+ setFullyLoaded(true);
290
+ return;
291
+ }
292
+
293
+ let initialSelectedOptions: any[] = [];
294
+
295
+ if (isMultiple) {
296
+ // For multiple, expect array
297
+ if (Array.isArray(currentValue)) {
298
+ initialSelectedOptions = currentValue;
299
+ } else if (currentValue) {
300
+ // Single value in multiple mode - wrap in array
301
+ initialSelectedOptions = [currentValue];
302
+ }
303
+ } else {
304
+ // For single, wrap in array for internal processing
305
+ if (currentValue) {
306
+ initialSelectedOptions = [currentValue];
307
+ }
308
+ }
309
+
310
+ console.log('🎯 SearchableSelectChips DEBUG - Selected options set:', {
311
+ resource,
312
+ currentValue,
313
+ initialSelectedOptions,
314
+ isMultiple,
315
+ timestamp: new Date().toISOString()
316
+ });
317
+
318
+ setSelectedOptions(initialSelectedOptions);
319
+ setFullyLoaded(true);
320
+ }, [value, defaultValues, isMultiple]);
321
+
322
+ // Update selected options when parsedOptions change and we have IDs but no full objects
323
+ useEffect(() => {
324
+ if (parsedOptions.length > 0 && selectedOptions.length > 0) {
325
+ const updatedOptions = selectedOptions.map(selectedOption => {
326
+ // If it's already a full object, keep it
327
+ if (typeof selectedOption === 'object' && selectedOption[viewAttribute]) {
328
+ return selectedOption;
329
+ }
330
+
331
+ // If it's just an ID, find the full object
332
+ const id = typeof selectedOption === 'object' ? selectedOption[valueKeyId] : selectedOption;
333
+ return parsedOptions.find(option => option[valueKeyId] === id) || selectedOption;
334
+ });
335
+
336
+ setSelectedOptions(updatedOptions);
337
+ }
338
+ }, [parsedOptions, viewAttribute, valueKeyId]);
339
+
340
+ // Debounced search
341
+ const debounce = (fn: Function, delay = 500) => {
342
+ clearTimeout(timeOutSearch);
343
+ setTimeOutSearch(
344
+ setTimeout(() => {
345
+ fn();
346
+ }, delay)
347
+ );
348
+ };
349
+
350
+ // FIXED: Simplified update field value
351
+ const updateFieldValue = (newSelectedOptions: any[]) => {
352
+ setSelectedOptions(newSelectedOptions || []);
353
+
354
+ if (onChange) {
355
+ if (isMultiple) {
356
+ // For multiple, send the array as-is
357
+ onChange(newSelectedOptions || []);
358
+ } else {
359
+ // For single, send the first item or null
360
+ onChange(newSelectedOptions && newSelectedOptions.length > 0 ? newSelectedOptions[0] : null);
361
+ }
362
+ }
363
+ };
364
+
365
+ if (!fullyLoaded) return <LoadingIndicator />;
366
+
367
+ return (
368
+ <Autocomplete
369
+ multiple={isMultiple}
370
+ loading={isResourceSearchLoading}
371
+ fullWidth={fullWidth}
372
+ freeSolo={false}
373
+ options={parsedOptions}
374
+ open={open}
375
+ onOpen={() => setOpen(true)}
376
+ onClose={() => setOpen(false)}
377
+ value={isMultiple ? selectedOptions : (selectedOptions[0] || null)}
378
+ isOptionEqualToValue={isOptionEqualToValue}
379
+ autoHighlight
380
+ disabled={disabled}
381
+ getOptionLabel={(option: any) => {
382
+ if (!option) return '';
383
+ return renderText(_transformOption(option, "optionlabel"), "optionlabel") || '';
384
+ }}
385
+ renderOption={(props, option: any) => {
386
+ const { key, ...newProps } = props as any;
387
+ const isSearchMatch = option.isSearchMatch;
388
+ const hasSearchQuery = q && q.length >= minSearch;
389
+
390
+ return (
391
+ <Box
392
+ key={option.key || `option-${option[valueKeyId]}`}
393
+ component="li"
394
+ sx={{
395
+ "& > img": { mr: 2, flexShrink: 0 },
396
+ // 🔥 ENHANCED: Better visual distinction for mixed results
397
+ fontWeight: hasSearchQuery && isSearchMatch ? 'bold' : 'normal',
398
+ opacity: hasSearchQuery && !isSearchMatch ? 0.7 : 1,
399
+ borderLeft: hasSearchQuery && isSearchMatch ? '3px solid #1976d2' : 'none',
400
+ paddingLeft: hasSearchQuery && isSearchMatch ? 1 : 2,
401
+ backgroundColor: hasSearchQuery && isSearchMatch ? 'rgba(25, 118, 210, 0.04)' : 'transparent',
402
+ // 🔥 NEW: Add subtle separator between search matches and other options
403
+ borderTop: hasSearchQuery && !isSearchMatch && option.priority === 2 ? '1px solid rgba(0,0,0,0.1)' : 'none',
404
+ marginTop: hasSearchQuery && !isSearchMatch && option.priority === 2 ? 0.5 : 0,
405
+ }}
406
+ {...newProps}
407
+ >
408
+ {/* 🔥 ENHANCED: Show section headers for mixed results */}
409
+ {hasSearchQuery && option.priority === 2 && parsedOptions.indexOf(option) === parsedOptions.findIndex(opt => opt.priority === 2) && (
410
+ <Box
411
+ sx={{
412
+ position: 'absolute',
413
+ top: -20,
414
+ left: 0,
415
+ right: 0,
416
+ fontSize: '0.7rem',
417
+ color: 'text.secondary',
418
+ backgroundColor: 'background.paper',
419
+ padding: '2px 8px',
420
+ borderBottom: '1px solid rgba(0,0,0,0.1)',
421
+ fontWeight: 'bold',
422
+ }}
423
+ >
424
+ Otras opciones
425
+ </Box>
426
+ )}
427
+
428
+ {renderText(_transformOption(option, "option"), "option")}
429
+
430
+ {/* 🔥 ENHANCED: Better indicators for search matches */}
431
+ {hasSearchQuery && isSearchMatch && (
432
+ <Box
433
+ component="span"
434
+ sx={{
435
+ ml: 'auto',
436
+ fontSize: '0.75rem',
437
+ color: '#1976d2',
438
+ fontWeight: 'bold',
439
+ display: 'flex',
440
+ alignItems: 'center',
441
+ gap: 0.5
442
+ }}
443
+ >
444
+ <Box
445
+ component="span"
446
+ sx={{
447
+ width: 6,
448
+ height: 6,
449
+ borderRadius: '50%',
450
+ backgroundColor: '#1976d2',
451
+ }}
452
+ />
453
+ Coincidencia
454
+ </Box>
455
+ )}
456
+ </Box>
457
+ );
458
+ }}
459
+ // FIXED: Use modern renderValue instead of deprecated renderTags
460
+ renderValue={(tagValue, getTagProps) => {
461
+ return tagValue.map((option, index) => {
462
+ const chipProps = getTagProps({ index });
463
+ return (
464
+ <Chip
465
+ key={`chip-${option[valueKeyId] || index}`}
466
+ label={renderText(_transformOption(option, "chip"), "chip")}
467
+ {...chipProps}
468
+ />
469
+ );
470
+ });
471
+ }}
472
+ onChange={(event: any, rawValue) => {
473
+ console.log('🔄 SearchableSelectChips DEBUG - Autocomplete onChange:', {
474
+ resource,
475
+ rawValue,
476
+ isMultiple,
477
+ timestamp: new Date().toISOString()
478
+ });
479
+
480
+ // FIXED: Simple handling - let Autocomplete handle the value format
481
+ if (isMultiple) {
482
+ updateFieldValue(Array.isArray(rawValue) ? rawValue : []);
483
+ } else {
484
+ updateFieldValue(rawValue ? [rawValue] : []);
485
+ }
486
+ }}
487
+ renderInput={(params) => (
488
+ <TextField
489
+ {...params}
490
+ label={selectLabel}
491
+ variant="outlined"
492
+ placeholder={placeholder}
493
+ onChange={(ev) => {
494
+ const searchValue = ev.target.value;
495
+
496
+ console.log('🔍 SearchableSelectChips DEBUG - Search input changed:', {
497
+ resource,
498
+ searchValue,
499
+ searchLength: searchValue.length,
500
+ minSearch,
501
+ willTriggerSearch: searchValue.length >= minSearch,
502
+ timestamp: new Date().toISOString()
503
+ });
504
+
505
+ if (
506
+ searchValue &&
507
+ searchValue !== "" &&
508
+ searchValue.length >= minSearch
509
+ ) {
510
+ debounce(() => {
511
+ console.log('🚀 SearchableSelectChips DEBUG - Triggering search:', {
512
+ resource,
513
+ searchValue,
514
+ minOptions,
515
+ searchResults,
516
+ timestamp: new Date().toISOString()
517
+ });
518
+ setQ(searchValue);
519
+ }, timerSearch);
520
+ } else if (searchValue === "") {
521
+ console.log('🔄 SearchableSelectChips DEBUG - Clearing search:', {
522
+ resource,
523
+ timestamp: new Date().toISOString()
524
+ });
525
+ setQ("");
526
+ }
527
+ }}
528
+ // FIXED: Use modern slotProps instead of deprecated InputProps
529
+ slotProps={{
530
+ input: {
531
+ ...params.InputProps,
532
+ autoComplete: "new-password",
533
+ endAdornment: (
534
+ <>
535
+ {isResourceSearchLoading && (
536
+ <CircularProgress color="inherit" size={20} />
537
+ )}
538
+ {params.InputProps.endAdornment}
539
+ </>
540
+ ),
541
+ },
542
+ }}
543
+ />
544
+ )}
545
+ // 🔥 NEW: Add custom props for better mixed results UX
546
+ ListboxProps={{
547
+ sx: {
548
+ maxHeight: 400,
549
+ '& .MuiAutocomplete-option': {
550
+ position: 'relative',
551
+ }
552
+ }
553
+ }}
554
+ />
555
+ );
556
+ };
557
+
558
+ export default SearchableSelectChips;