goobs-frontend 0.9.16 → 0.9.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "goobs-frontend",
3
- "version": "0.9.16",
3
+ "version": "0.9.18",
4
4
  "type": "module",
5
5
  "description": "A comprehensive React-based libary that extends the functionality of Material-UI",
6
6
  "license": "MIT",
@@ -35,11 +35,11 @@
35
35
  "lodash": "^4",
36
36
  "next": "15",
37
37
  "otplib": "^12",
38
- "react-datepicker": "^7",
39
- "react-native": "^0.78.0",
38
+ "react-datepicker": "^8",
39
+ "react-native": "^0.78",
40
40
  "react-qr-code": "^2",
41
41
  "slate": "^0.112",
42
- "slate-dom": "^0.111",
42
+ "slate-dom": "^0.112",
43
43
  "slate-history": "^0.110",
44
44
  "slate-react": "^0.112",
45
45
  "storybook": "^8",
@@ -1,6 +1,6 @@
1
1
  'use client'
2
2
 
3
- import React, { useState } from 'react'
3
+ import React, { useState, useMemo } from 'react'
4
4
  import { Box, Alert } from '@mui/material'
5
5
  import CustomToolbar from '../Toolbar'
6
6
  import Table from './Table'
@@ -24,7 +24,16 @@ function DataGrid({
24
24
  onManage,
25
25
  onShow,
26
26
  onSelectionChange,
27
+ showIdColumns = false,
27
28
  }: DatagridProps) {
29
+ // Filter columns to hide ID columns based on showIdColumns prop
30
+ const filteredColumns = useMemo(() => {
31
+ if (showIdColumns) {
32
+ return columns
33
+ }
34
+ return columns.filter(col => col.field !== 'id' && col.field !== '_id')
35
+ }, [columns, showIdColumns])
36
+
28
37
  // Local state
29
38
  const [rows, setRows] = useState<RowData[]>(providedRows || [])
30
39
  const [selectedRows, setSelectedRows] = useState<string[]>([])
@@ -32,7 +41,7 @@ function DataGrid({
32
41
  const [pageSize, setPageSize] = useState(10)
33
42
 
34
43
  // Initialize columns/rows if needed
35
- useInitializeGrid({ columns, providedRows, setRows })
44
+ useInitializeGrid({ columns: filteredColumns, providedRows, setRows })
36
45
 
37
46
  // 1) When row selection changes
38
47
  const handleSelectionChange = (newSelectedIds: string[]) => {
@@ -60,7 +69,7 @@ function DataGrid({
60
69
 
61
70
  // 2) Search logic
62
71
  const { filteredRows, updatedSearchbarProps } = useSearchbar({
63
- columns,
72
+ columns: filteredColumns,
64
73
  rows,
65
74
  searchbarProps,
66
75
  })
@@ -138,7 +147,7 @@ function DataGrid({
138
147
  >
139
148
  {/* Table component */}
140
149
  <Table
141
- columns={columns}
150
+ columns={filteredColumns}
142
151
  rows={visibleRows}
143
152
  selectedRowIds={selectedRows}
144
153
  onRowClick={handleRowClick}
@@ -154,7 +163,7 @@ function DataGrid({
154
163
  rowCount={filteredRows.length}
155
164
  onPageChange={setPage}
156
165
  onPageSizeChange={setPageSize}
157
- columns={columns}
166
+ columns={filteredColumns}
158
167
  />
159
168
  </Box>
160
169
  </Box>
@@ -48,6 +48,9 @@ export interface DatagridProps {
48
48
  searchbarProps?: SearchbarProps
49
49
  error?: Error | null
50
50
 
51
+ // Controls whether ID columns (id/_id) are visible
52
+ showIdColumns?: boolean
53
+
51
54
  // Single or multi selection callbacks:
52
55
  onManage?: () => void
53
56
  onShow?: () => void
@@ -53,6 +53,8 @@ export interface DropdownProps extends Omit<FormControlProps, 'onChange'> {
53
53
  value?: string
54
54
  width?: string
55
55
  disabled?: boolean
56
+ // Controls whether ID columns (containing 'id' or '_id') are visible by default
57
+ showIdColumns?: boolean
56
58
  }
57
59
 
58
60
  const StyledFormControl = styled(FormControl)<{ width?: string }>(
@@ -173,10 +175,28 @@ const Dropdown: React.FC<DropdownProps> = ({
173
175
  value: externalValue,
174
176
  width,
175
177
  disabled = false,
178
+ showIdColumns = false, // Default to hiding ID columns for security
176
179
  }) => {
177
180
  const [selectedValue, setSelectedValue] = useState<string>('')
178
181
  const [focused, setFocused] = useState(false)
179
182
 
183
+ // Filter out options with id values if showIdColumns is false
184
+ const filteredOptions = React.useMemo(() => {
185
+ if (showIdColumns) {
186
+ return options
187
+ }
188
+ // Hide options where the value is exactly 'id' or '_id', or looks like a database ID
189
+ return options.filter(opt => {
190
+ const value = opt.value.toLowerCase()
191
+ // Check if value is an ID field or looks like an ObjectId
192
+ return !(
193
+ value === 'id' ||
194
+ value === '_id' ||
195
+ /^[0-9a-f]{24}$/.test(value) // MongoDB ObjectId format
196
+ )
197
+ })
198
+ }, [options, showIdColumns])
199
+
180
200
  useEffect(() => {
181
201
  if (externalValue !== undefined) {
182
202
  setSelectedValue(externalValue)
@@ -351,7 +371,7 @@ const Dropdown: React.FC<DropdownProps> = ({
351
371
  fontcolor={fontcolor}
352
372
  disabled={disabled}
353
373
  >
354
- {options.map(renderMenuItem)}
374
+ {filteredOptions.map(renderMenuItem)}
355
375
  </StyledSelect>
356
376
  {helperText && (
357
377
  <FormHelperText error={error}>{helperText}</FormHelperText>
@@ -66,6 +66,8 @@ export interface SearchableDropdownProps {
66
66
  searchHistory?: HistoryItem[] | string[]
67
67
  onSearch?: (searchTerm: string, timestamp?: Date) => void
68
68
  maxHistoryItems?: number
69
+ // Controls whether ID columns (containing 'id' or '_id') are visible by default
70
+ showIdColumns?: boolean
69
71
  }
70
72
 
71
73
  const StyledFormControl = styled(FormControl)<{ width?: string }>(
@@ -277,6 +279,7 @@ const SearchableDropdown: React.FC<SearchableDropdownProps> = ({
277
279
  searchHistory = [], // Default to empty array
278
280
  onSearch,
279
281
  maxHistoryItems = 5, // Default to showing 5 history items
282
+ showIdColumns = false, // Default to hiding ID columns for security
280
283
  }) => {
281
284
  const [value, setValue] = useState<DropdownOption | string | null>(null)
282
285
  const [inputValue, setInputValue] = useState('')
@@ -561,6 +564,23 @@ const SearchableDropdown: React.FC<SearchableDropdownProps> = ({
561
564
  return localHistory
562
565
  }, [searchHistory, localHistory])
563
566
 
567
+ // Filter out options with id values if showIdColumns is false
568
+ const filteredBaseOptions = React.useMemo(() => {
569
+ if (showIdColumns) {
570
+ return options
571
+ }
572
+ // Hide options where the value is exactly 'id' or '_id', or looks like a database ID
573
+ return options.filter(opt => {
574
+ const value = opt.value.toLowerCase()
575
+ // Check if value is an ID field or looks like an ObjectId (MongoDB ID format)
576
+ return !(
577
+ value === 'id' ||
578
+ value === '_id' ||
579
+ /^[0-9a-f]{24}$/.test(value)
580
+ )
581
+ })
582
+ }, [options, showIdColumns])
583
+
564
584
  // Create a combined options array based on active tab and input value
565
585
  const getFilteredOptions = React.useCallback(() => {
566
586
  const currentInputVal = inputValue.trim()
@@ -581,7 +601,7 @@ const SearchableDropdown: React.FC<SearchableDropdownProps> = ({
581
601
  // Map history items to dropdown options
582
602
  return combinedHistory.map(item => {
583
603
  // Check if this history item matches any of the original options
584
- const matchingOption = options.find(
604
+ const matchingOption = filteredBaseOptions.find(
585
605
  opt =>
586
606
  opt.value.toLowerCase() === item.text.toLowerCase() ||
587
607
  (
@@ -617,11 +637,11 @@ const SearchableDropdown: React.FC<SearchableDropdownProps> = ({
617
637
  // ALL OPTIONS TAB (activeTab === 0)
618
638
  // If input is empty, return all options
619
639
  if (!currentInputVal) {
620
- return options
640
+ return filteredBaseOptions
621
641
  }
622
642
 
623
643
  // Filter options based on current input
624
- const filteredOpts = options.filter(opt =>
644
+ const filteredOpts = filteredBaseOptions.filter(opt =>
625
645
  opt.value.toLowerCase().includes(currentInputVal.toLowerCase())
626
646
  )
627
647
 
@@ -654,7 +674,7 @@ const SearchableDropdown: React.FC<SearchableDropdownProps> = ({
654
674
  }
655
675
 
656
676
  return filteredOpts
657
- }, [inputValue, combinedHistory, options, activeTab, variant])
677
+ }, [inputValue, combinedHistory, filteredBaseOptions, activeTab, variant])
658
678
 
659
679
  // Create the footer component for the dropdown with tabs
660
680
  const ListboxFooter = React.forwardRef<HTMLDivElement>((_, ref) => (
@@ -4,7 +4,7 @@ import React, { useState, useCallback } from 'react'
4
4
  import { Close } from '@mui/icons-material'
5
5
  import { Dialog, IconButton, Box, useMediaQuery, useTheme } from '@mui/material'
6
6
  import Typography from '../../../../../Typography'
7
- import Dropdown from '../../../../../Field/Dropdown/Regular'
7
+ import SearchableDropdown from '../../../../../Field/Dropdown/Searchable'
8
8
  import MultiSelect from '../../../../../Field/Dropdown/MultiSelect'
9
9
  import ComplexTextEditor from '../../../../../ComplexTextEditor'
10
10
  import CustomButton from '../../../../../Button'
@@ -64,39 +64,116 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
64
64
  const [taskTitle, setTaskTitle] = useState('')
65
65
  const [taskDescription, setTaskDescription] = useState('')
66
66
 
67
+ // Debug logging for incoming props
68
+ console.log('AdministratorAddTaskCompanyDropdown - Props received:', {
69
+ statusesCount: statuses?.length || 0,
70
+ subStatusesCount: subStatuses?.length || 0,
71
+ statusesData: statuses,
72
+ topicsData: topics,
73
+ })
74
+
67
75
  // ------------------ DROPDOWN OPTIONS ------------------
68
76
  const companyOptions = rawCompanies.map(c => ({
69
- value: c._id,
70
- label: c.companyName,
77
+ value: c.companyName,
71
78
  }))
79
+
72
80
  const severityOptions = severityLevels.map(sl => ({
73
81
  value: String(sl.severityLevel),
74
- attribute1: sl._id,
82
+ attribute1: sl.description || '',
75
83
  }))
84
+
76
85
  const statusOptions = statuses.map(s => ({
77
86
  value: s.status,
78
- attribute1: s._id,
79
- }))
80
- const subStatusOptions = subStatuses.map(s => ({
81
- value: s.subStatus,
82
- attribute1: s._id,
83
87
  }))
88
+
89
+ // Filter substatuses based on the selected status
90
+ const filteredSubStatusOptions = subStatuses
91
+ .filter(s => {
92
+ // If no status is selected, hide all substatuses
93
+ if (!selectedStatus) return false
94
+
95
+ // Get the ID of the selected status
96
+ const selectedStatusId = statuses.find(
97
+ status => status.status === selectedStatus
98
+ )?._id
99
+ console.log('Filtering substatuses by status:', {
100
+ selectedStatus,
101
+ selectedStatusId,
102
+ substatus: s.subStatus,
103
+ substatusStatusId: s.statusId,
104
+ isMatch: s.statusId === selectedStatusId,
105
+ })
106
+
107
+ // Only include substatuses with the matching statusId
108
+ return s.statusId === selectedStatusId
109
+ })
110
+ .map(s => {
111
+ const associatedStatus =
112
+ statuses.find(status => status._id === s.statusId)?.status || ''
113
+
114
+ return {
115
+ value: s.subStatus,
116
+ attribute1: associatedStatus,
117
+ }
118
+ })
119
+
120
+ // Add a "no substatuses" option if none are available for the selected status
121
+ const finalSubStatusOptions =
122
+ filteredSubStatusOptions.length > 0
123
+ ? filteredSubStatusOptions
124
+ : selectedStatus
125
+ ? [
126
+ {
127
+ value: 'No substatuses available for this status',
128
+ attribute1: '',
129
+ },
130
+ ]
131
+ : []
132
+
133
+ console.log('Filtered substatus options:', finalSubStatusOptions)
134
+
84
135
  const queueOptions = schedulingQueues.map(q => ({
85
136
  value: q.queueName,
86
- attribute1: q._id,
87
137
  }))
88
138
 
139
+ // Effect to reset substatus when status changes
140
+ React.useEffect(() => {
141
+ // Clear the selected substatus when the status changes
142
+ setSelectedSubStatus('')
143
+ }, [selectedStatus])
144
+
89
145
  // ------------------ SUBMIT HANDLER ------------------
90
146
  const handleSubmit = useCallback(() => {
147
+ // Find the IDs from the selected values
148
+ const selectedStatusId =
149
+ statuses.find(s => s.status === selectedStatus)?._id || ''
150
+ const selectedSubStatusId =
151
+ subStatuses.find(s => s.subStatus === selectedSubStatus)?._id || ''
152
+ const selectedQueueId =
153
+ schedulingQueues.find(q => q.queueName === selectedQueue)?._id || ''
154
+ const selectedCompanyId =
155
+ rawCompanies.find(c => c.companyName === selectedCompany)?._id || ''
156
+
157
+ console.log('Submitting task with mapped IDs:', {
158
+ statusValue: selectedStatus,
159
+ statusId: selectedStatusId,
160
+ subStatusValue: selectedSubStatus,
161
+ subStatusId: selectedSubStatusId,
162
+ queueValue: selectedQueue,
163
+ queueId: selectedQueueId,
164
+ companyValue: selectedCompany,
165
+ companyId: selectedCompanyId,
166
+ })
167
+
91
168
  const newTaskData: Omit<Task, '_id'> = {
92
169
  title: taskTitle,
93
170
  description: taskDescription,
94
171
  topicIds: selectedTopicIds,
95
172
  articleIds: selectedArticleIds,
96
173
  severityId: selectedSeverity || '',
97
- schedulingQueueId: selectedQueue || '',
98
- statusId: selectedStatus || '',
99
- substatusId: selectedSubStatus || '',
174
+ schedulingQueueId: selectedQueueId,
175
+ statusId: selectedStatusId,
176
+ substatusId: selectedSubStatusId,
100
177
  employeeIds: [],
101
178
  createdAt: new Date(),
102
179
  updatedAt: new Date(),
@@ -113,14 +190,10 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
113
190
  kbArticles: [],
114
191
  teamMember: '',
115
192
  nextActionDate: '',
116
- companyId: '',
193
+ companyId: selectedCompanyId,
117
194
  customerId: '',
118
195
  }
119
196
 
120
- newTaskData.companyId = selectedCompany
121
- newTaskData.customerId = ''
122
- newTaskData.employeeIds = []
123
-
124
197
  onAdd(newTaskData)
125
198
  }, [
126
199
  taskTitle,
@@ -134,6 +207,10 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
134
207
  selectedCompany,
135
208
  createdUserId,
136
209
  onAdd,
210
+ statuses,
211
+ subStatuses,
212
+ schedulingQueues,
213
+ rawCompanies,
137
214
  ])
138
215
 
139
216
  // ------------------ RENDER ------------------
@@ -158,12 +235,10 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
158
235
  position: 'absolute',
159
236
  right: 8,
160
237
  top: 8,
161
- color: theme => theme.palette.grey[500],
162
- zIndex: theme => theme.zIndex.modal + 1,
238
+ color: theme.palette.grey[500],
239
+ zIndex: theme.zIndex.modal + 1,
163
240
  cursor: 'pointer',
164
- '&:hover': {
165
- color: theme => theme.palette.grey[700],
166
- },
241
+ '&:hover': { color: theme.palette.grey[700] },
167
242
  }}
168
243
  >
169
244
  <Close />
@@ -192,11 +267,16 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
192
267
  />
193
268
 
194
269
  {/* Company Dropdown */}
195
- <Dropdown
270
+ <SearchableDropdown
196
271
  label="Company"
197
272
  options={companyOptions}
198
- value={selectedCompany}
199
- onChange={e => setSelectedCompany(e.target.value)}
273
+ defaultValue={
274
+ companyOptions.find(opt => opt.value === selectedCompany)?.value
275
+ }
276
+ onChange={option => {
277
+ setSelectedCompany(option?.value || '')
278
+ }}
279
+ placeholder="Select a company"
200
280
  />
201
281
 
202
282
  {/* Top row of fields */}
@@ -216,44 +296,36 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
216
296
  gap: 1,
217
297
  }}
218
298
  >
219
- <Dropdown
299
+ <SearchableDropdown
220
300
  label="Severity Level"
221
301
  options={severityOptions}
222
- value={
302
+ defaultValue={
223
303
  severityOptions.find(
224
304
  opt => opt.attribute1 === selectedSeverity
225
- )?.value || ''
305
+ )?.value
226
306
  }
227
- onChange={e => {
228
- const selectedValue = e.target.value
229
- const option = severityOptions.find(
230
- opt => opt.value === selectedValue
231
- )
232
- if (option) {
233
- setSelectedSeverity(option.attribute1)
234
- } else {
235
- setSelectedSeverity('')
236
- }
307
+ onChange={option => {
308
+ setSelectedSeverity(option?.attribute1 || '')
237
309
  }}
310
+ placeholder="Select severity level"
238
311
  />
239
- <Dropdown
312
+ <SearchableDropdown
240
313
  label="Status"
241
314
  options={statusOptions}
242
- value={
243
- statusOptions.find(opt => opt.attribute1 === selectedStatus)
244
- ?.value || ''
315
+ defaultValue={
316
+ statusOptions.find(opt => opt.value === selectedStatus)?.value
245
317
  }
246
- onChange={e => {
247
- const selectedValue = e.target.value
248
- const option = statusOptions.find(
249
- opt => opt.value === selectedValue
250
- )
251
- if (option) {
252
- setSelectedStatus(option.attribute1)
253
- } else {
254
- setSelectedStatus('')
255
- }
318
+ onChange={option => {
319
+ const newStatus = option?.value || ''
320
+ console.log('Status selected:', newStatus)
321
+
322
+ // Find the status ID for the selected status
323
+ const statusObj = statuses.find(s => s.status === newStatus)
324
+ console.log('Selected status object:', statusObj)
325
+
326
+ setSelectedStatus(newStatus)
256
327
  }}
328
+ placeholder="Select status"
257
329
  />
258
330
  </Box>
259
331
 
@@ -266,63 +338,127 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
266
338
  gap: 1,
267
339
  }}
268
340
  >
269
- <Dropdown
341
+ <SearchableDropdown
270
342
  label="Associated Product (Queue)"
271
343
  options={queueOptions}
272
- value={
273
- queueOptions.find(opt => opt.attribute1 === selectedQueue)
274
- ?.value || ''
344
+ defaultValue={
345
+ queueOptions.find(opt => opt.value === selectedQueue)?.value
275
346
  }
276
- onChange={e => {
277
- const selectedValue = e.target.value
278
- const option = queueOptions.find(
279
- opt => opt.value === selectedValue
280
- )
281
- if (option) {
282
- setSelectedQueue(option.attribute1)
283
- } else {
284
- setSelectedQueue('')
285
- }
347
+ onChange={option => {
348
+ setSelectedQueue(option?.value || '')
286
349
  }}
350
+ placeholder="Select product queue"
287
351
  />
288
- <Dropdown
352
+ <SearchableDropdown
289
353
  label="Substatus"
290
- options={subStatusOptions}
291
- value={
292
- subStatusOptions.find(
293
- opt => opt.attribute1 === selectedSubStatus
294
- )?.value || ''
354
+ options={finalSubStatusOptions}
355
+ defaultValue={
356
+ finalSubStatusOptions.find(
357
+ opt => opt.value === selectedSubStatus
358
+ )?.value
295
359
  }
296
- onChange={e => {
297
- const selectedValue = e.target.value
298
- const option = subStatusOptions.find(
299
- opt => opt.value === selectedValue
300
- )
301
- if (option) {
302
- setSelectedSubStatus(option.attribute1)
303
- } else {
304
- setSelectedSubStatus('')
305
- }
360
+ onChange={option => {
361
+ setSelectedSubStatus(option?.value || '')
306
362
  }}
363
+ placeholder={
364
+ selectedStatus
365
+ ? 'Select substatus'
366
+ : 'Please select a status first'
367
+ }
368
+ disabled={!selectedStatus}
307
369
  />
308
370
  </Box>
309
371
  </Box>
310
372
 
311
- {/* Topics multi-select using raw topic IDs */}
312
- <MultiSelect
313
- label="Topics"
314
- options={topics.map(t => t._id)}
315
- defaultSelected={selectedTopicIds}
316
- onChange={setSelectedTopicIds}
317
- />
373
+ {/* Create a mapping from topic name to ID for lookup when submitting */}
374
+ {/* Also create a reverse mapping from ID to name for displaying selected values */}
375
+ {React.useMemo(() => {
376
+ console.log('Topics being mapped for dropdown:', topics)
318
377
 
319
- {/* Knowledgebase Articles multi-select using raw article IDs */}
320
- <MultiSelect
321
- label="Knowledgebase Articles"
322
- options={knowledgebaseArticles.map(a => a._id)}
323
- defaultSelected={selectedArticleIds}
324
- onChange={setSelectedArticleIds}
325
- />
378
+ // Create mappings for topic names to IDs and back
379
+ const topicNameToId: Record<string, string> = {}
380
+ const topicIdToName: Record<string, string> = {}
381
+
382
+ topics.forEach(t => {
383
+ const displayName = t.topic || `Topic ${t._id}`
384
+ topicNameToId[displayName] = t._id
385
+ topicIdToName[t._id] = displayName
386
+ })
387
+
388
+ console.log('Topic mappings created:', {
389
+ topicNameToId,
390
+ topicIdToName,
391
+ })
392
+
393
+ // Create user-friendly display options for topics
394
+ const topicOptions = topics.map(t => t.topic || `Topic ${t._id}`)
395
+
396
+ // Translate selected IDs to names for display
397
+ const selectedTopicNames = selectedTopicIds.map(
398
+ id => topicIdToName[id] || id
399
+ )
400
+
401
+ return (
402
+ <>
403
+ {/* Topics multi-select – using topic names with ID mapping */}
404
+ <MultiSelect
405
+ label="Topics"
406
+ options={topicOptions}
407
+ defaultSelected={selectedTopicNames}
408
+ onChange={selectedNames => {
409
+ console.log('Selected topic names:', selectedNames)
410
+
411
+ // Map the selected names back to IDs
412
+ const newSelectedIds = selectedNames.map(
413
+ name => topicNameToId[name] || name // Fallback to name if mapping not found
414
+ )
415
+ console.log('Mapped to topic IDs:', newSelectedIds)
416
+
417
+ setSelectedTopicIds(newSelectedIds)
418
+ }}
419
+ />
420
+ </>
421
+ )
422
+ }, [topics, selectedTopicIds])}
423
+
424
+ {/* Knowledgebase Articles multi-select – using article titles now instead of IDs */}
425
+ {React.useMemo(() => {
426
+ // Create mappings for article titles to IDs and back
427
+ const articleTitleToId: Record<string, string> = {}
428
+ const articleIdToTitle: Record<string, string> = {}
429
+
430
+ knowledgebaseArticles.forEach(a => {
431
+ const title = a.articleTitle || `Article ${a._id}`
432
+ articleTitleToId[title] = a._id
433
+ articleIdToTitle[a._id] = title
434
+ })
435
+
436
+ // Create user-friendly display options for articles
437
+ const articleOptions = knowledgebaseArticles.map(
438
+ a => a.articleTitle || `Article ${a._id}`
439
+ )
440
+
441
+ // Translate selected IDs to titles for display
442
+ const selectedArticleTitles = selectedArticleIds.map(
443
+ id => articleIdToTitle[id] || id
444
+ )
445
+
446
+ return (
447
+ <MultiSelect
448
+ label="Knowledgebase Articles"
449
+ options={articleOptions}
450
+ defaultSelected={selectedArticleTitles}
451
+ onChange={selectedTitles => {
452
+ // Map the selected titles back to IDs
453
+ const newSelectedIds = selectedTitles.map(
454
+ title => articleTitleToId[title] || title // Fallback to title if mapping not found
455
+ )
456
+
457
+ setSelectedArticleIds(newSelectedIds)
458
+ }}
459
+ />
460
+ )
461
+ }, [knowledgebaseArticles, selectedArticleIds])}
326
462
 
327
463
  {/* Action Buttons */}
328
464
  <Box