@faststore/core 4.3.0-dev.1 → 4.3.0-dev.3

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 (32) hide show
  1. package/.turbo/turbo-generate.log +4 -4
  2. package/.turbo/turbo-test.log +99 -29
  3. package/@generated/cached-operations.json +3 -1
  4. package/@generated/gql.ts +29 -5
  5. package/@generated/graphql.ts +129 -8
  6. package/@generated/persisted-documents.json +5 -1
  7. package/@generated/schema.graphql +69 -0
  8. package/CHANGELOG.md +12 -0
  9. package/cms/faststore/pages/cms_content_type__landingpage.jsonc +6 -0
  10. package/cms/faststore/schema.json +123 -91
  11. package/cms/faststore/sections.json +5 -0
  12. package/package.json +6 -6
  13. package/src/components/auth/ProfileChallenge/ProfileChallenge.tsx +2 -2
  14. package/src/components/search/SearchInput/SearchInput.tsx +139 -332
  15. package/src/components/sections/Navbar/Navbar.tsx +1 -0
  16. package/src/components/sections/Navbar/section.module.scss +3 -0
  17. package/src/sdk/auth/index.ts +2 -2
  18. package/src/sdk/orderEntry/useOrderEntry.ts +58 -0
  19. package/src/sdk/orderEntry/useOrderEntryOperation.ts +132 -0
  20. package/src/sdk/orderEntry/useOrderEntryUpload.ts +96 -0
  21. package/src/sdk/orderEntry/useOrderFormItems.ts +57 -0
  22. package/src/styles/global/index.scss +16 -0
  23. package/test/components/auth/ProfileChallenge.test.tsx +51 -0
  24. package/test/components/search/SearchInput.test.ts +72 -0
  25. package/test/components/search/SearchInputComponent.test.tsx +276 -0
  26. package/test/sdk/auth/useAuth.test.ts +94 -0
  27. package/test/sdk/orderEntry/useOrderEntry.test.ts +139 -0
  28. package/test/sdk/orderEntry/useOrderEntryOperation.test.ts +205 -0
  29. package/test/sdk/orderEntry/useOrderEntryUpload.test.ts +142 -0
  30. package/test/sdk/orderEntry/useOrderFormItems.test.ts +132 -0
  31. package/test/server/index.test.ts +4 -0
  32. package/src/sdk/product/useBulkProductsQuery.ts +0 -128
@@ -27,16 +27,13 @@ import {
27
27
  Icon as UIIcon,
28
28
  IconButton as UIIconButton,
29
29
  SearchInput as UISearchInput,
30
- useCSVParser,
31
30
  useOnClickOutside,
32
- useUI,
33
31
  } from '@faststore/ui'
34
32
 
35
33
  import type {
36
- CSVData,
37
- Product,
38
34
  SearchInputFieldProps as UISearchInputFieldProps,
39
35
  SearchInputFieldRef as UISearchInputFieldRef,
36
+ Product,
40
37
  } from '@faststore/ui'
41
38
 
42
39
  import type { SearchProviderContextValue } from '@faststore/ui'
@@ -46,10 +43,13 @@ import useSearchHistory from 'src/sdk/search/useSearchHistory'
46
43
  import useSuggestions from 'src/sdk/search/useSuggestions'
47
44
 
48
45
  import { cartStore } from 'src/sdk/cart'
49
- import { convertProductToQuickOrder } from 'src/sdk/product/convertProductToQuickOrder'
50
- import { useBulkProductsQuery } from 'src/sdk/product/useBulkProductsQuery'
46
+ import type { CartItem } from 'src/sdk/cart'
51
47
  import { usePriceFormatter } from 'src/sdk/product/useFormattedPrice'
52
- import { useFormatSearchPath } from 'src/sdk/search/formatSearchPath'
48
+ import { useAuth } from 'src/sdk/auth'
49
+ import { useOrderEntry } from 'src/sdk/orderEntry/useOrderEntry'
50
+ import { useOrderFormItems } from 'src/sdk/orderEntry/useOrderFormItems'
51
+ import type { OrderFormCartItem } from 'src/sdk/orderEntry/useOrderFormItems'
52
+ import { formatSearchPath } from 'src/sdk/search/formatSearchPath'
53
53
  import { formatFileName, formatFileSize } from 'src/utils/utilities'
54
54
 
55
55
  const SearchDropdown = lazy(
@@ -74,9 +74,6 @@ export type SearchInputProps = {
74
74
  loadingLabel?: string
75
75
  searchHistoryTitle?: string
76
76
  searchTopTitle?: string
77
- // Called when the user clicks Search in the file upload card, with the parsed CSV data.
78
- // Use this to run bulk search, add to cart, or analytics.
79
- onFileSearch?: (data: CSVData) => void
80
77
  } & Omit<UISearchInputFieldProps, 'onSubmit' | 'attachmentButtonIcon'>
81
78
 
82
79
  export type SearchInputRef = UISearchInputFieldRef & {
@@ -92,6 +89,22 @@ const sendAnalytics = async (term: string) => {
92
89
  })
93
90
  }
94
91
 
92
+ export function mapOrderFormItemsToProducts(
93
+ items: OrderFormCartItem[]
94
+ ): Product[] {
95
+ return items.map((item) => ({
96
+ id: item.id,
97
+ name: item.name,
98
+ price: item.price,
99
+ quantityUpdated: false,
100
+ image: { url: item.imageUrl ?? '', alternateName: item.name },
101
+ inventory: item.availability === 'available' ? 9999 : 0,
102
+ availability:
103
+ item.availability === 'available' ? 'available' : 'outOfStock',
104
+ selectedCount: item.quantity,
105
+ }))
106
+ }
107
+
95
108
  const SearchInput = forwardRef<SearchInputRef, SearchInputProps>(
96
109
  function SearchInput(
97
110
  {
@@ -101,11 +114,6 @@ const SearchInput = forwardRef<SearchInputRef, SearchInputProps>(
101
114
  sort,
102
115
  placeholder,
103
116
  quickOrderSettings,
104
- submitButtonAriaLabel,
105
- loadingLabel,
106
- searchHistoryTitle,
107
- searchTopTitle,
108
- onFileSearch,
109
117
  ...otherProps
110
118
  },
111
119
  ref
@@ -124,40 +132,55 @@ const SearchInput = forwardRef<SearchInputRef, SearchInputProps>(
124
132
  const [hasFile, setHasFile] = useState(false)
125
133
  const [isQuickOrderDrawerOpen, setIsQuickOrderDrawerOpen] = useState(false)
126
134
  const [quickOrderProducts, setQuickOrderProducts] = useState<Product[]>([])
127
- const [noProductsError, setNoProductsError] = useState<boolean>(false)
135
+ const [oesOrderFormItems, setOesOrderFormItems] = useState<
136
+ OrderFormCartItem[]
137
+ >([])
128
138
 
129
139
  const searchRef = useRef<HTMLDivElement>(null)
130
140
  const { addToSearchHistory } = useSearchHistory()
131
141
  const router = useRouter()
132
- const formatSearchPath = useFormatSearchPath()
133
142
  const priceFormatter = usePriceFormatter()
134
- const { pushToast } = useUI()
135
143
 
136
- const [csvData, setCsvData] = useState<CSVData | null>(null)
137
144
  const [selectedFile, setSelectedFile] = useState<File | null>(null)
138
- const [skusToFetch, setSkusToFetch] = useState<string[]>([])
139
- const [skuQuantityMap, setSkuQuantityMap] = useState<
140
- Record<string, number>
141
- >({})
142
- const [isLoadingWithDelay, setIsLoadingWithDelay] = useState(false)
143
-
144
- const csvParserOptions = useMemo(
145
- () => ({ delimiter: ',' as const, skipEmptyLines: true }),
146
- []
147
- )
148
145
 
149
- const csvParser = useCSVParser(csvParserOptions)
150
146
  const {
151
- error: csvError,
152
- isParsing: isCsvProcessing,
153
- onParseFile,
154
- onClearError,
155
- onGenerateTemplate,
156
- } = csvParser
157
-
158
- const isQuickOrderEnabled = quickOrderSettings?.quickOrder ?? false
147
+ submitFile,
148
+ status: oesStatus,
149
+ isUploading: isOESUploading,
150
+ isProcessing: isOESProcessing,
151
+ error: oesError,
152
+ reset: resetOES,
153
+ } = useOrderEntry()
154
+
155
+ const {
156
+ fetchOrderFormItems,
157
+ items: orderFormItems,
158
+ reset: resetOrderFormItems,
159
+ } = useOrderFormItems()
160
+
161
+ useEffect(() => {
162
+ if (
163
+ (oesStatus?.status === 'SUCCESS' ||
164
+ oesStatus?.status === 'PARTIAL_SUCCESS') &&
165
+ oesStatus.entityId
166
+ ) {
167
+ fetchOrderFormItems(oesStatus.entityId)
168
+ }
169
+ }, [oesStatus?.status, oesStatus?.entityId, fetchOrderFormItems])
170
+
171
+ useEffect(() => {
172
+ if (!orderFormItems || orderFormItems.length === 0) return
173
+ setOesOrderFormItems(orderFormItems)
174
+ setQuickOrderProducts(mapOrderFormItemsToProducts(orderFormItems))
175
+ setFileUploadVisible(false)
176
+ setIsUploadOpen(false)
177
+ setIsQuickOrderDrawerOpen(true)
178
+ }, [orderFormItems])
179
+
180
+ const { isAuthenticated } = useAuth()
181
+ const isQuickOrderEnabled =
182
+ (quickOrderSettings?.quickOrder ?? false) && isAuthenticated
159
183
  const attachmentButton = quickOrderSettings?.attachmentButton
160
- const toastMessages = quickOrderSettings?.toastMessages
161
184
  const drawerConfig = quickOrderSettings?.drawer
162
185
  const a11yLabels = quickOrderSettings?.accessibilityLabels
163
186
  const fileUploadCardConfig = quickOrderSettings?.fileUploadCard
@@ -166,16 +189,6 @@ const SearchInput = forwardRef<SearchInputRef, SearchInputProps>(
166
189
  resetSearchInput: () => setSearchQuery(''),
167
190
  }))
168
191
 
169
- // Map CSV parser error types to FileUploadErrorType
170
- const mapCSVErrorToFileUploadErrorType = (
171
- csvErrorType?: string
172
- ): FileUploadErrorType => {
173
- if (csvErrorType === 'FILE_ERROR') {
174
- return FileUploadErrorType.Unreadable
175
- }
176
- return FileUploadErrorType.InvalidStructure
177
- }
178
-
179
192
  const onSearchSelection: SearchProviderContextValue['onSearchSelection'] = (
180
193
  term,
181
194
  path
@@ -185,252 +198,31 @@ const SearchInput = forwardRef<SearchInputRef, SearchInputProps>(
185
198
  setSearchDropdownVisible(false)
186
199
  }
187
200
 
188
- const handleFileSelect = async (files: File[]) => {
201
+ const handleFileSelect = (files: File[]) => {
189
202
  if (files.length === 0) return
190
-
191
203
  setHasFile(true)
192
204
  setIsUploadOpen(true)
193
-
194
- const file = files[0]
195
- setSelectedFile(file)
196
-
197
- onClearError()
198
- setCsvData(null)
199
- setQuickOrderProducts([])
200
- setSkusToFetch([])
201
- setSkuQuantityMap({})
202
- setIsQuickOrderDrawerOpen(false)
203
- setNoProductsError(false)
204
-
205
- const result = await onParseFile(file)
206
-
207
- if (result && result.data && result.data.length > 0) {
208
- setCsvData(result)
209
- }
210
- }
211
-
212
- const handleDownloadTemplate = async () => {
213
- try {
214
- const csvContent = await onGenerateTemplate()
215
-
216
- if (csvContent) {
217
- const blob = new Blob([csvContent], { type: 'text/csv' })
218
- const url = window.URL.createObjectURL(blob)
219
- const a = document.createElement('a')
220
- a.href = url
221
- a.download = 'template.csv'
222
- document.body.appendChild(a)
223
- a.click()
224
- document.body.removeChild(a)
225
- window.URL.revokeObjectURL(url)
226
- }
227
- } catch (error) {
228
- console.error('Failed to download template:', error)
229
- }
205
+ setSelectedFile(files[0])
206
+ resetOES()
230
207
  }
231
208
 
232
209
  const handleDismiss = () => {
233
- setCsvData(null)
234
210
  setSelectedFile(null)
235
- setSkusToFetch([])
236
- setSkuQuantityMap({})
237
211
  setQuickOrderProducts([])
212
+ setOesOrderFormItems([])
238
213
  setFileUploadVisible(false)
239
214
  setHasFile(false)
240
215
  setIsUploadOpen(false)
241
- setNoProductsError(false)
242
- onClearError()
216
+ resetOES()
217
+ resetOrderFormItems()
243
218
  }
244
219
 
245
220
  const handleSearch = async (_file?: File) => {
246
- let dataToUse = csvData
247
-
248
- if (!dataToUse || !dataToUse.data || dataToUse.data.length === 0) {
249
- const fileToParse = _file || selectedFile
250
-
251
- if (!fileToParse) {
252
- pushToast({
253
- title: toastMessages?.noFileSelected?.title,
254
- message: toastMessages?.noFileSelected?.message,
255
- status: 'ERROR',
256
- icon: <UIIcon name="CircleWavyWarning" width={30} height={30} />,
257
- })
258
- return
259
- }
260
-
261
- try {
262
- const parsePromise = onParseFile(fileToParse)
263
-
264
- const timeoutPromise = new Promise<never>((_, reject) => {
265
- setTimeout(() => {
266
- reject(new Error(toastMessages?.fileTimeout?.message))
267
- }, 30000)
268
- })
269
-
270
- const result = (await Promise.race([
271
- parsePromise,
272
- timeoutPromise,
273
- ])) as Awaited<ReturnType<typeof onParseFile>>
274
-
275
- if (result && result.data && result.data.length > 0) {
276
- dataToUse = result
277
- setCsvData(result)
278
- } else {
279
- pushToast({
280
- title: toastMessages?.noDataFound?.title,
281
- message: toastMessages?.noDataFound?.message,
282
- status: 'ERROR',
283
- icon: <UIIcon name="CircleWavyWarning" width={30} height={30} />,
284
- })
285
- return
286
- }
287
- } catch (error) {
288
- const errorMessage =
289
- error instanceof Error
290
- ? error.message
291
- : toastMessages?.fileProcessingError?.defaultMessage
292
- pushToast({
293
- title: toastMessages?.fileProcessingError?.title,
294
- message: errorMessage,
295
- status: 'ERROR',
296
- icon: <UIIcon name="CircleWavyWarning" width={30} height={30} />,
297
- })
298
- return
299
- }
300
- }
301
-
302
- if (!dataToUse || !dataToUse.data || dataToUse.data.length === 0) {
303
- pushToast({
304
- title: toastMessages?.noDataAvailable?.title,
305
- message: toastMessages?.noDataAvailable?.message,
306
- status: 'ERROR',
307
- icon: <UIIcon name="CircleWavyWarning" width={30} height={30} />,
308
- })
309
- return
310
- }
311
-
312
- const payload = {
313
- fileName: dataToUse.fileName,
314
- totalRows: dataToUse.totalRows,
315
- fileSize: dataToUse.fileSize,
316
- data: dataToUse.data,
317
- }
318
- try {
319
- window.dispatchEvent(
320
- new CustomEvent('faststore:file-search', { detail: payload })
321
- )
322
- } catch {
323
- // ignore in envs without window
324
- }
325
- onFileSearch?.(dataToUse)
326
-
327
- const map: Record<string, number> = {}
328
- for (const item of dataToUse.data) {
329
- const sku = item.sku?.trim()
330
- if (!sku) continue
331
- map[sku] = (map[sku] ?? 0) + (item.quantity ?? 1)
332
- }
333
- const uniqueSkus = Object.keys(map)
334
-
335
- if (uniqueSkus.length === 0) {
336
- pushToast({
337
- title: toastMessages?.noValidSkus?.title,
338
- message: toastMessages?.noValidSkus?.message,
339
- status: 'ERROR',
340
- icon: <UIIcon name="CircleWavyWarning" width={30} height={30} />,
341
- })
342
- return
343
- }
344
-
345
- setQuickOrderProducts([])
346
- setNoProductsError(false)
347
- setIsLoadingWithDelay(true)
348
- setSkuQuantityMap(map)
349
- // Open drawer immediately to show loading skeleton
350
- setIsQuickOrderDrawerOpen(true)
351
- setFileUploadVisible(false)
352
- setSkusToFetch(uniqueSkus)
221
+ const file = _file ?? selectedFile
222
+ if (!file) return
223
+ await submitFile(file)
353
224
  }
354
225
 
355
- const { products: fetchedProducts, isLoading: isLoadingProducts } =
356
- useBulkProductsQuery(skusToFetch)
357
-
358
- useEffect(() => {
359
- if (skusToFetch.length > 0 && isLoadingProducts) {
360
- // Clear products and show loading skeleton
361
- setQuickOrderProducts([])
362
- setIsLoadingWithDelay(true)
363
- // Keep drawer open to show loading skeleton
364
- setIsQuickOrderDrawerOpen(true)
365
- setFileUploadVisible(false)
366
- setNoProductsError(false)
367
- return
368
- }
369
-
370
- if (
371
- !isLoadingProducts &&
372
- skusToFetch.length > 0 &&
373
- Object.keys(skuQuantityMap).length > 0 &&
374
- fetchedProducts.length > 0
375
- ) {
376
- // Add artificial delay to test loading state
377
- setIsLoadingWithDelay(true)
378
-
379
- const timeoutId = setTimeout(() => {
380
- const convertedProducts: Product[] = []
381
-
382
- fetchedProducts.forEach((productData) => {
383
- if (productData.product && !productData.error) {
384
- const requestedQuantity =
385
- skuQuantityMap[productData.sku ?? ''] ?? 1
386
-
387
- const convertedProduct = convertProductToQuickOrder(
388
- productData.product,
389
- requestedQuantity
390
- )
391
-
392
- if (convertedProduct) {
393
- convertedProducts.push(convertedProduct)
394
- }
395
- }
396
- })
397
-
398
- setQuickOrderProducts(convertedProducts)
399
- setIsLoadingWithDelay(false)
400
-
401
- if (convertedProducts.length > 0) {
402
- setIsQuickOrderDrawerOpen(true)
403
- setFileUploadVisible(false)
404
- setNoProductsError(false)
405
- } else {
406
- // Keep drawer open to show empty state message
407
- setQuickOrderProducts([])
408
- setIsQuickOrderDrawerOpen(true)
409
- setFileUploadVisible(false)
410
- setNoProductsError(true)
411
- }
412
- }, 2000) // 2 second delay for testing
413
-
414
- return () => {
415
- clearTimeout(timeoutId)
416
- }
417
- }
418
-
419
- if (
420
- !isLoadingProducts &&
421
- skusToFetch.length > 0 &&
422
- Object.keys(skuQuantityMap).length > 0 &&
423
- fetchedProducts.length === 0
424
- ) {
425
- // Keep drawer open to show empty state message
426
- setQuickOrderProducts([])
427
- setIsQuickOrderDrawerOpen(true)
428
- setFileUploadVisible(false)
429
- setNoProductsError(true)
430
- setIsLoadingWithDelay(false)
431
- }
432
- }, [fetchedProducts, skusToFetch, skuQuantityMap, isLoadingProducts])
433
-
434
226
  useOnClickOutside(searchRef, () => {
435
227
  setSearchDropdownVisible(customSearchDropdownVisibleCondition ?? false)
436
228
  setFileUploadVisible(false)
@@ -451,48 +243,60 @@ const SearchInput = forwardRef<SearchInputRef, SearchInputProps>(
451
243
  const buttonProps = {
452
244
  onClick: onSearchClick,
453
245
  testId: buttonTestId,
454
- 'aria-label': submitButtonAriaLabel,
455
246
  }
456
247
 
457
248
  const handleAddToCart = useCallback(
458
249
  (productsToAdd: Product[]) => {
459
- productsToAdd.forEach((product: Product) => {
250
+ for (const product of productsToAdd) {
460
251
  if (
461
- product.selectedCount > 0 &&
462
- product.availability === 'available'
463
- ) {
464
- const fetchedProduct = fetchedProducts.find(
465
- (p) => p.product?.sku === product.id
466
- )?.product
467
-
468
- if (fetchedProduct && fetchedProduct.offers?.offers[0]) {
469
- const offer = fetchedProduct.offers.offers[0]
470
-
471
- cartStore.addItem({
472
- itemOffered: {
473
- sku: fetchedProduct.sku,
474
- name: fetchedProduct.name,
475
- unitMultiplier: fetchedProduct.unitMultiplier ?? 1,
476
- image: fetchedProduct.image,
477
- brand: fetchedProduct.brand,
478
- isVariantOf: fetchedProduct.isVariantOf,
479
- gtin: fetchedProduct.gtin,
480
- additionalProperty: fetchedProduct.additionalProperty,
252
+ product.selectedCount <= 0 ||
253
+ product.availability !== 'available'
254
+ )
255
+ continue
256
+
257
+ const originalItem = oesOrderFormItems.find(
258
+ (item) => item.id === product.id
259
+ )
260
+
261
+ const cartItem: Omit<CartItem, 'id'> = {
262
+ itemOffered: {
263
+ sku: product.id,
264
+ name: product.name,
265
+ unitMultiplier: originalItem?.unitMultiplier ?? 1,
266
+ image: [
267
+ {
268
+ url: product.image.url,
269
+ alternateName: product.image.alternateName,
481
270
  },
482
- seller: offer.seller,
483
- quantity: product.selectedCount,
484
- price: product.price,
485
- listPrice: offer.listPrice ?? product.price,
486
- priceWithTaxes: offer.priceWithTaxes ?? product.price,
487
- listPriceWithTaxes: offer.listPriceWithTaxes ?? product.price,
488
- })
489
- }
271
+ ],
272
+ brand: { name: '' },
273
+ isVariantOf: {
274
+ productGroupID: '',
275
+ name: product.name,
276
+ skuVariants: {
277
+ activeVariations: {},
278
+ slugsMap: {},
279
+ availableVariations: {},
280
+ },
281
+ },
282
+ gtin: '',
283
+ additionalProperty: [],
284
+ },
285
+ seller: { identifier: originalItem?.seller ?? '1' },
286
+ quantity: product.selectedCount,
287
+ price: product.price,
288
+ priceWithTaxes: product.price,
289
+ listPrice: originalItem?.listPrice ?? product.price,
290
+ listPriceWithTaxes: originalItem?.listPrice ?? product.price,
490
291
  }
491
- })
492
292
 
293
+ cartStore.addItem(cartItem)
294
+ }
493
295
  setIsQuickOrderDrawerOpen(false)
296
+ setQuickOrderProducts([])
297
+ setOesOrderFormItems([])
494
298
  },
495
- [fetchedProducts]
299
+ [oesOrderFormItems]
496
300
  )
497
301
 
498
302
  const getCompletedStatusText = useCallback(
@@ -521,9 +325,7 @@ const SearchInput = forwardRef<SearchInputRef, SearchInputProps>(
521
325
  {hidden ? (
522
326
  <UIIconButton
523
327
  type="submit"
524
- aria-label={
525
- a11yLabels?.searchButtonAriaLabel ?? submitButtonAriaLabel
526
- }
328
+ aria-label={a11yLabels?.searchButtonAriaLabel ?? 'Search'}
527
329
  icon={<UIIcon name="MagnifyingGlass" />}
528
330
  size="small"
529
331
  {...buttonProps}
@@ -585,9 +387,6 @@ const SearchInput = forwardRef<SearchInputRef, SearchInputProps>(
585
387
  <SearchDropdown
586
388
  sort={sort as SearchState['sort']}
587
389
  quickOrderSettings={quickOrderSettings}
588
- loadingLabel={loadingLabel}
589
- searchHistoryTitle={searchHistoryTitle}
590
- searchTopTitle={searchTopTitle}
591
390
  onChangeCustomSearchDropdownVisible={
592
391
  setCustomSearchDropdownVisibleCondition
593
392
  }
@@ -611,23 +410,25 @@ const SearchInput = forwardRef<SearchInputRef, SearchInputProps>(
611
410
  }
612
411
  searchButtonLabel={fileUploadCardConfig?.searchButtonLabel}
613
412
  uploadingStatusText={fileUploadCardConfig?.uploadingStatusText}
413
+ processingStatusText={
414
+ fileUploadCardConfig?.processingStatusText ?? 'Importing...'
415
+ }
614
416
  getCompletedStatusText={getCompletedStatusText}
615
417
  errorMessages={resolvedErrorMessages}
616
418
  accept={fileUploadCardConfig?.acceptedFileTypes}
617
419
  isOpen={isUploadOpen || hasFile || fileUploadVisible}
618
420
  onDismiss={handleDismiss}
619
421
  onFileSelect={handleFileSelect}
620
- onDownloadTemplate={handleDownloadTemplate}
621
422
  formatterFileSize={formatFileSize}
622
423
  formatterFileName={formatFileName}
623
424
  onSearch={handleSearch}
624
- isUploading={isCsvProcessing || isLoadingProducts}
625
- hasError={(!!csvError || noProductsError) && !isLoadingProducts}
626
- {...((csvError || (noProductsError && !isLoadingProducts)) && {
627
- errorType: noProductsError
628
- ? FileUploadErrorType.NoProductsFound
629
- : mapCSVErrorToFileUploadErrorType(csvError.type),
630
- errorMessage: noProductsError ? undefined : csvError?.message,
425
+ isUploading={isOESUploading}
426
+ isProcessing={isOESProcessing}
427
+ hasError={!!oesError || oesStatus?.status === 'FAILED'}
428
+ {...((oesError || oesStatus?.status === 'FAILED') && {
429
+ errorType: FileUploadErrorType.Unreadable,
430
+ errorMessage:
431
+ oesError?.message ?? oesStatus?.message ?? undefined,
631
432
  })}
632
433
  />
633
434
  )}
@@ -639,16 +440,23 @@ const SearchInput = forwardRef<SearchInputRef, SearchInputProps>(
639
440
  onClick: () => {
640
441
  setIsQuickOrderDrawerOpen(false)
641
442
  setQuickOrderProducts([])
642
- setSkusToFetch([])
643
- setSkuQuantityMap({})
443
+ setOesOrderFormItems([])
644
444
  },
645
445
  }}
646
446
  providerProps={{
647
447
  initialProducts: quickOrderProducts,
648
- isLoading: isLoadingProducts || isLoadingWithDelay,
649
- totalRequestedSkus: Object.keys(skuQuantityMap).length || 0,
448
+ isLoading: false,
449
+ totalRequestedSkus: 0,
650
450
  onAddToCart: handleAddToCart,
651
- alertMessages: drawerConfig?.alertMessages,
451
+ alertMessages: {
452
+ ...drawerConfig?.alertMessages,
453
+ ...(oesStatus?.status === 'PARTIAL_SUCCESS' && {
454
+ notFound:
455
+ oesStatus.message ??
456
+ drawerConfig?.alertMessages?.notFound ??
457
+ 'Some items could not be imported.',
458
+ }),
459
+ },
652
460
  }}
653
461
  >
654
462
  <QuickOrderDrawerHeader
@@ -660,8 +468,7 @@ const SearchInput = forwardRef<SearchInputRef, SearchInputProps>(
660
468
  onCloseDrawer={() => {
661
469
  setIsQuickOrderDrawerOpen(false)
662
470
  setQuickOrderProducts([])
663
- setSkusToFetch([])
664
- setSkuQuantityMap({})
471
+ setOesOrderFormItems([])
665
472
  }}
666
473
  />
667
474
  <QuickOrderDrawerProducts
@@ -108,6 +108,7 @@ export interface NavbarProps {
108
108
  removeButtonAriaLabel?: string
109
109
  searchButtonLabel?: string
110
110
  uploadingStatusText?: string
111
+ processingStatusText?: string
111
112
  completedStatusTemplate?: string
112
113
  acceptedFileTypes?: string
113
114
  errorMessages?: Partial<
@@ -36,6 +36,9 @@
36
36
  @include meta.load-css("~@faststore/ui/src/components/organisms/Navbar/styles.scss");
37
37
  @include meta.load-css("~@faststore/ui/src/components/organisms/SlideOver/styles.scss");
38
38
  @include meta.load-css("~@faststore/ui/src/components/organisms/SKUMatrix/styles.scss");
39
+ @include meta.load-css("~@faststore/ui/src/components/molecules/Card/styles.scss");
40
+ @include meta.load-css("~@faststore/ui/src/components/molecules/FileUploadCard/styles.scss");
41
+ @include meta.load-css("~@faststore/ui/src/components/molecules/FileUploadStatus/styles.scss");
39
42
  @include meta.load-css("~@faststore/ui/src/components/organisms/QuickOrderDrawer/styles.scss");
40
43
 
41
44
  // Sets Navbar height on desktop to avoid CLS issue - Cumulative Layout Shift
@@ -17,7 +17,7 @@ export const useAuth = () => {
17
17
  Boolean(channel.salesChannel) &&
18
18
  channel.hasOnlyDefaultSalesChannel === false
19
19
 
20
- const isAutenticated = hasSalesChannel && person
20
+ const isAuthenticated = hasSalesChannel && person
21
21
 
22
- return { isAutenticated, profile: person, channel, isValidating }
22
+ return { isAuthenticated, profile: person, channel, isValidating }
23
23
  }