goobs-frontend 0.9.23 → 0.9.24

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.23",
3
+ "version": "0.9.24",
4
4
  "type": "module",
5
5
  "description": "A comprehensive React-based libary that extends the functionality of Material-UI",
6
6
  "license": "MIT",
@@ -10,12 +10,42 @@ import FormControl, { FormControlProps } from '@mui/material/FormControl'
10
10
  import Select, { SelectChangeEvent } from '@mui/material/Select'
11
11
  import Chip from '@mui/material/Chip'
12
12
 
13
+ /**
14
+ * Interface for dropdown options with attributes, matching SearchableDropdown format
15
+ */
16
+ export interface DropdownOption {
17
+ value: string
18
+ attribute1?: string
19
+ attribute2?: string
20
+ attribute3?: string
21
+ attribute4?: string
22
+ attribute5?: string
23
+ attribute6?: string
24
+ uniqueKey?: string
25
+ }
26
+
13
27
  export interface MultiSelectChipProps
14
28
  extends Omit<FormControlProps, 'onChange'> {
15
29
  label?: React.ReactNode
16
- options?: string[]
30
+ /**
31
+ * Can be either:
32
+ * - string[] for simple options
33
+ * - DropdownOption[] for complex options with attributes
34
+ */
35
+ options?: string[] | DropdownOption[]
36
+ /**
37
+ * The values of the selected items
38
+ */
17
39
  defaultSelected?: string[]
40
+ /**
41
+ * Callback when selection changes - returns array of selected values
42
+ */
18
43
  onChange?: (values: string[]) => void
44
+ /**
45
+ * Whether options are complex DropdownOption objects or simple strings
46
+ * Defaults to auto-detect
47
+ */
48
+ complexOptions?: boolean
19
49
 
20
50
  backgroundcolor?: string
21
51
  outlinecolor?: string
@@ -27,6 +57,10 @@ export interface MultiSelectChipProps
27
57
 
28
58
  shrunklabelposition?: 'onNotch' | 'aboveNotch'
29
59
  sx?: SxProps
60
+ /**
61
+ * Whether to show option details in the dropdown - only applies to complex options
62
+ */
63
+ showOptionDetails?: boolean
30
64
  }
31
65
 
32
66
  const ITEM_HEIGHT = 40
@@ -146,6 +180,8 @@ export default function MultipleSelectChip(props: MultiSelectChipProps) {
146
180
  options = [],
147
181
  defaultSelected = [],
148
182
  onChange,
183
+ complexOptions: userSpecifiedComplexOptions,
184
+ showOptionDetails = false,
149
185
 
150
186
  backgroundcolor,
151
187
  outlinecolor,
@@ -163,6 +199,48 @@ export default function MultipleSelectChip(props: MultiSelectChipProps) {
163
199
  const [selectedValues, setSelectedValues] =
164
200
  React.useState<string[]>(defaultSelected)
165
201
 
202
+ // Auto-detect if options are complex (DropdownOption[]) or simple (string[])
203
+ const isComplexOptions = React.useMemo(() => {
204
+ if (userSpecifiedComplexOptions !== undefined) {
205
+ return userSpecifiedComplexOptions
206
+ }
207
+ // Check if options is an array and the first item is an object with a value property
208
+ return (
209
+ options.length > 0 &&
210
+ typeof options[0] !== 'string' &&
211
+ 'value' in options[0]
212
+ )
213
+ }, [options, userSpecifiedComplexOptions])
214
+
215
+ // Parse options to get display values and lookup
216
+ const optionsData = React.useMemo(() => {
217
+ if (!isComplexOptions) {
218
+ // Simple string options
219
+ return {
220
+ displayOptions: options as string[],
221
+ optionsMap: new Map<string, string>(),
222
+ }
223
+ }
224
+
225
+ // Complex options with attributes
226
+ const complexOptions = options as DropdownOption[]
227
+ // Use DropdownOption type explicitly for the map
228
+ const optionsMap = new Map<string, DropdownOption>()
229
+
230
+ // Create a map of value to original option object for quick lookups
231
+ complexOptions.forEach(option => {
232
+ optionsMap.set(option.value, option)
233
+ })
234
+
235
+ // Extract just the values for the dropdown
236
+ const displayOptions = complexOptions.map(option => option.value)
237
+
238
+ return {
239
+ displayOptions,
240
+ optionsMap,
241
+ }
242
+ }, [options, isComplexOptions])
243
+
166
244
  const hasValue = React.useMemo(
167
245
  () => (selectedValues.length > 0).toString(),
168
246
  [selectedValues]
@@ -179,6 +257,35 @@ export default function MultipleSelectChip(props: MultiSelectChipProps) {
179
257
  }
180
258
  }
181
259
 
260
+ // Render menu item text appropriately based on option type
261
+ const renderMenuItemText = (value: string) => {
262
+ if (!isComplexOptions) {
263
+ return value
264
+ }
265
+
266
+ // Cast optionsMap to the correct type with DropdownOption
267
+ const optionsMap = optionsData.optionsMap as Map<string, DropdownOption>
268
+ const option = optionsMap.get(value)
269
+
270
+ // If no option found in the map, just return the value
271
+ if (!option) return value
272
+
273
+ // Check if we should show details and if attribute1 exists
274
+ if (!showOptionDetails || typeof option.attribute1 === 'undefined') {
275
+ return value
276
+ }
277
+
278
+ // If showing details, include attribute1 (typically description/department)
279
+ return (
280
+ <Box>
281
+ <Box>{value}</Box>
282
+ <Box sx={{ fontSize: '0.8em', color: 'text.secondary' }}>
283
+ {option.attribute1}
284
+ </Box>
285
+ </Box>
286
+ )
287
+ }
288
+
182
289
  return (
183
290
  <Box
184
291
  sx={{
@@ -251,13 +358,13 @@ export default function MultipleSelectChip(props: MultiSelectChipProps) {
251
358
  )}
252
359
  MenuProps={MenuProps}
253
360
  >
254
- {options.map(name => (
361
+ {optionsData.displayOptions.map(value => (
255
362
  <MenuItem
256
- key={name}
257
- value={name}
258
- style={getStyles(name, selectedValues, theme)}
363
+ key={value}
364
+ value={value}
365
+ style={getStyles(value, selectedValues, theme)}
259
366
  >
260
- {name}
367
+ {renderMenuItemText(value)}
261
368
  </MenuItem>
262
369
  ))}
263
370
  </Select>
@@ -55,10 +55,15 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
55
55
 
56
56
  // ------------------ FORM STATE ------------------
57
57
  const [selectedCompany, setSelectedCompany] = useState('')
58
+ const [selectedCompanyId, setSelectedCompanyId] = useState('')
58
59
  const [selectedSeverity, setSelectedSeverity] = useState('')
60
+ const [selectedSeverityId, setSelectedSeverityId] = useState('')
59
61
  const [selectedQueue, setSelectedQueue] = useState('')
62
+ const [selectedQueueId, setSelectedQueueId] = useState('')
60
63
  const [selectedStatus, setSelectedStatus] = useState('')
64
+ const [selectedStatusId, setSelectedStatusId] = useState('')
61
65
  const [selectedSubStatus, setSelectedSubStatus] = useState('')
66
+ const [selectedSubStatusId, setSelectedSubStatusId] = useState('')
62
67
  const [selectedTopicIds, setSelectedTopicIds] = useState<string[]>([])
63
68
  const [selectedArticleIds, setSelectedArticleIds] = useState<string[]>([])
64
69
  const [taskTitle, setTaskTitle] = useState('')
@@ -75,15 +80,18 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
75
80
  // ------------------ DROPDOWN OPTIONS ------------------
76
81
  const companyOptions = rawCompanies.map(c => ({
77
82
  value: c.companyName,
83
+ attribute1: c._id,
78
84
  }))
79
85
 
80
86
  const severityOptions = severityLevels.map(sl => ({
81
87
  value: String(sl.severityLevel),
82
88
  attribute1: sl.description || '',
89
+ attribute2: sl._id,
83
90
  }))
84
91
 
85
92
  const statusOptions = statuses.map(s => ({
86
93
  value: s.status,
94
+ attribute1: s._id,
87
95
  }))
88
96
 
89
97
  // Filter substatuses based on the selected status
@@ -114,6 +122,7 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
114
122
  return {
115
123
  value: s.subStatus,
116
124
  attribute1: associatedStatus,
125
+ attribute2: s._id,
117
126
  }
118
127
  })
119
128
 
@@ -126,6 +135,7 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
126
135
  {
127
136
  value: 'No substatuses available for this status',
128
137
  attribute1: '',
138
+ attribute2: '',
129
139
  },
130
140
  ]
131
141
  : []
@@ -134,27 +144,19 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
134
144
 
135
145
  const queueOptions = schedulingQueues.map(q => ({
136
146
  value: q.queueName,
147
+ attribute1: q._id,
137
148
  }))
138
149
 
139
150
  // Effect to reset substatus when status changes
140
151
  React.useEffect(() => {
141
152
  // Clear the selected substatus when the status changes
142
153
  setSelectedSubStatus('')
154
+ setSelectedSubStatusId('')
143
155
  }, [selectedStatus])
144
156
 
145
157
  // ------------------ SUBMIT HANDLER ------------------
146
158
  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:', {
159
+ console.log('Submitting task with stored IDs:', {
158
160
  statusValue: selectedStatus,
159
161
  statusId: selectedStatusId,
160
162
  subStatusValue: selectedSubStatus,
@@ -163,14 +165,35 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
163
165
  queueId: selectedQueueId,
164
166
  companyValue: selectedCompany,
165
167
  companyId: selectedCompanyId,
168
+ severityValue: selectedSeverity,
169
+ severityId: selectedSeverityId,
166
170
  })
167
171
 
172
+ // Validate required fields before submission
173
+ if (!selectedSeverityId) {
174
+ console.error('Error: Severity Level is required')
175
+ alert('Please select a Severity Level')
176
+ return
177
+ }
178
+
179
+ if (!selectedStatusId) {
180
+ console.error('Error: Status is required')
181
+ alert('Please select a Status')
182
+ return
183
+ }
184
+
185
+ if (!selectedSubStatusId) {
186
+ console.error('Error: Substatus is required')
187
+ alert('Please select a Substatus')
188
+ return
189
+ }
190
+
168
191
  const newTaskData: Omit<Task, '_id'> = {
169
192
  title: taskTitle,
170
193
  description: taskDescription,
171
194
  topicIds: selectedTopicIds,
172
195
  articleIds: selectedArticleIds,
173
- severityId: selectedSeverity || '',
196
+ severityId: selectedSeverityId,
174
197
  schedulingQueueId: selectedQueueId,
175
198
  statusId: selectedStatusId,
176
199
  substatusId: selectedSubStatusId,
@@ -200,17 +223,18 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
200
223
  taskDescription,
201
224
  selectedTopicIds,
202
225
  selectedArticleIds,
203
- selectedSeverity,
204
- selectedQueue,
226
+ selectedSeverityId,
227
+ selectedQueueId,
228
+ selectedStatusId,
229
+ selectedSubStatusId,
230
+ selectedCompanyId,
205
231
  selectedStatus,
206
232
  selectedSubStatus,
233
+ selectedQueue,
207
234
  selectedCompany,
235
+ selectedSeverity,
208
236
  createdUserId,
209
237
  onAdd,
210
- statuses,
211
- subStatuses,
212
- schedulingQueues,
213
- rawCompanies,
214
238
  ])
215
239
 
216
240
  // ------------------ RENDER ------------------
@@ -275,6 +299,7 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
275
299
  }
276
300
  onChange={option => {
277
301
  setSelectedCompany(option?.value || '')
302
+ setSelectedCompanyId(option?.attribute1 || '')
278
303
  }}
279
304
  placeholder="Select a company"
280
305
  />
@@ -301,11 +326,14 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
301
326
  options={severityOptions}
302
327
  defaultValue={
303
328
  severityOptions.find(
304
- opt => opt.attribute1 === selectedSeverity
329
+ opt => opt.attribute2 === selectedSeverityId
305
330
  )?.value
306
331
  }
307
332
  onChange={option => {
308
- setSelectedSeverity(option?.attribute1 || '')
333
+ // Store the severity level as display value and the ID properly
334
+ setSelectedSeverity(option?.value || '')
335
+ setSelectedSeverityId(option?.attribute2 || '')
336
+ console.log('Selected severity ID:', option?.attribute2)
309
337
  }}
310
338
  placeholder="Select severity level"
311
339
  />
@@ -313,17 +341,19 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
313
341
  label="Status"
314
342
  options={statusOptions}
315
343
  defaultValue={
316
- statusOptions.find(opt => opt.value === selectedStatus)?.value
344
+ statusOptions.find(opt => opt.attribute1 === selectedStatusId)
345
+ ?.value
317
346
  }
318
347
  onChange={option => {
319
348
  const newStatus = option?.value || ''
320
349
  console.log('Status selected:', newStatus)
321
350
 
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
351
  setSelectedStatus(newStatus)
352
+ setSelectedStatusId(option?.attribute1 || '')
353
+ console.log(
354
+ 'Selected status ID from attribute1:',
355
+ option?.attribute1
356
+ )
327
357
  }}
328
358
  placeholder="Select status"
329
359
  />
@@ -342,10 +372,13 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
342
372
  label="Associated Product (Queue)"
343
373
  options={queueOptions}
344
374
  defaultValue={
345
- queueOptions.find(opt => opt.value === selectedQueue)?.value
375
+ queueOptions.find(opt => opt.attribute1 === selectedQueueId)
376
+ ?.value
346
377
  }
347
378
  onChange={option => {
348
379
  setSelectedQueue(option?.value || '')
380
+ setSelectedQueueId(option?.attribute1 || '')
381
+ console.log('Selected queue ID:', option?.attribute1)
349
382
  }}
350
383
  placeholder="Select product queue"
351
384
  />
@@ -354,11 +387,13 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
354
387
  options={finalSubStatusOptions}
355
388
  defaultValue={
356
389
  finalSubStatusOptions.find(
357
- opt => opt.value === selectedSubStatus
390
+ opt => opt.attribute2 === selectedSubStatusId
358
391
  )?.value
359
392
  }
360
393
  onChange={option => {
361
394
  setSelectedSubStatus(option?.value || '')
395
+ setSelectedSubStatusId(option?.attribute2 || '')
396
+ console.log('Selected substatus ID:', option?.attribute2)
362
397
  }}
363
398
  placeholder={
364
399
  selectedStatus
@@ -375,87 +410,80 @@ const AdministratorAddTaskCompanyDropdown: React.FC<
375
410
  {React.useMemo(() => {
376
411
  console.log('Topics being mapped for dropdown:', topics)
377
412
 
378
- // Create mappings for topic names to IDs and back
379
- const topicNameToId: Record<string, string> = {}
380
- const topicIdToName: Record<string, string> = {}
413
+ // Create complex options for topics with _id as attribute1
414
+ const topicOptions = topics.map(t => ({
415
+ value: t.topic || `Topic ${t._id}`,
416
+ attribute1: t._id, // Store ID in attribute1
417
+ }))
381
418
 
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}`)
419
+ console.log('Topic options created:', topicOptions)
395
420
 
396
421
  // Translate selected IDs to names for display
397
- const selectedTopicNames = selectedTopicIds.map(
398
- id => topicIdToName[id] || id
399
- )
422
+ const selectedTopicValues = selectedTopicIds.map(id => {
423
+ const topic = topics.find(t => t._id === id)
424
+ return topic ? topic.topic || `Topic ${topic._id}` : id
425
+ })
400
426
 
401
427
  return (
402
428
  <>
403
- {/* Topics multi-select – using topic names with ID mapping */}
429
+ {/* Topics multi-select – using complex options with IDs in attribute1 */}
404
430
  <MultiSelect
405
431
  label="Topics"
406
432
  options={topicOptions}
407
- defaultSelected={selectedTopicNames}
408
- onChange={selectedNames => {
409
- console.log('Selected topic names:', selectedNames)
433
+ defaultSelected={selectedTopicValues}
434
+ onChange={selectedValues => {
435
+ console.log('Selected topic values:', selectedValues)
436
+
437
+ // Find the selected topics and get their IDs
438
+ const newSelectedIds = selectedValues.map(value => {
439
+ const matchingTopic = topicOptions.find(
440
+ opt => opt.value === value
441
+ )
442
+ return matchingTopic?.attribute1 || value // Fall back to value if no match
443
+ })
410
444
 
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
445
  console.log('Mapped to topic IDs:', newSelectedIds)
416
-
417
446
  setSelectedTopicIds(newSelectedIds)
418
447
  }}
448
+ complexOptions={true} // Explicitly set to use complex options
419
449
  />
420
450
  </>
421
451
  )
422
452
  }, [topics, selectedTopicIds])}
423
453
 
424
- {/* Knowledgebase Articles multi-select – using article titles now instead of IDs */}
454
+ {/* Knowledgebase Articles multi-select – using article titles with IDs in attribute1 */}
425
455
  {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
- )
456
+ // Create complex options with article IDs
457
+ const articleOptions = knowledgebaseArticles.map(a => ({
458
+ value: a.articleTitle || `Article ${a._id}`,
459
+ attribute1: a._id, // Store ID in attribute1
460
+ }))
440
461
 
441
462
  // Translate selected IDs to titles for display
442
- const selectedArticleTitles = selectedArticleIds.map(
443
- id => articleIdToTitle[id] || id
444
- )
463
+ const selectedArticleValues = selectedArticleIds.map(id => {
464
+ const article = knowledgebaseArticles.find(a => a._id === id)
465
+ return article
466
+ ? article.articleTitle || `Article ${article._id}`
467
+ : id
468
+ })
445
469
 
446
470
  return (
447
471
  <MultiSelect
448
472
  label="Knowledgebase Articles"
449
473
  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
- )
474
+ defaultSelected={selectedArticleValues}
475
+ onChange={selectedValues => {
476
+ // Map the selected values to IDs using attribute1
477
+ const newSelectedIds = selectedValues.map(value => {
478
+ const matchingArticle = articleOptions.find(
479
+ opt => opt.value === value
480
+ )
481
+ return matchingArticle?.attribute1 || value // Fall back to value if no match
482
+ })
456
483
 
457
484
  setSelectedArticleIds(newSelectedIds)
458
485
  }}
486
+ complexOptions={true} // Explicitly set to use complex options
459
487
  />
460
488
  )
461
489
  }, [knowledgebaseArticles, selectedArticleIds])}