@isoftdata/svelte-ecommerce 1.0.0-beta.4 → 1.0.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -56
- package/dist/EcommerceCategoryMapConfiguration.svelte +251 -251
- package/dist/EcommerceConditionMapConfiguration.svelte +192 -192
- package/dist/EcommerceConfiguration.svelte +204 -199
- package/dist/EcommerceDefaults.svelte +384 -352
- package/dist/EcommerceListingDetails.svelte +854 -852
- package/dist/EcommercePartTypeConfig.svelte +283 -277
- package/dist/EcommerceStoreConfiguration.svelte +251 -251
- package/dist/PolicyList.svelte +54 -54
- package/dist/helpers/listing.d.ts +19 -1
- package/dist/helpers/listing.js +112 -16
- package/dist/utils.d.ts +2 -1
- package/package.json +14 -13
|
@@ -1,852 +1,854 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { i18n } from 'i18next'
|
|
3
|
-
import { getContext } from 'svelte'
|
|
4
|
-
|
|
5
|
-
import Button from '@isoftdata/svelte-button'
|
|
6
|
-
import CurrencyInput from '@isoftdata/svelte-currency-input'
|
|
7
|
-
import Input from '@isoftdata/svelte-input'
|
|
8
|
-
import Modal from '@isoftdata/svelte-modal'
|
|
9
|
-
import Select from '@isoftdata/svelte-select'
|
|
10
|
-
import Textarea from '@isoftdata/svelte-textarea'
|
|
11
|
-
import PolicyList from './PolicyList.svelte'
|
|
12
|
-
import { ThumbnailGrid } from '@isoftdata/svelte-attachments'
|
|
13
|
-
import UserPrompt from '@isoftdata/svelte-user-prompt'
|
|
14
|
-
import { v4 as uuid } from '@lukeed/uuid'
|
|
15
|
-
import { ecommercePartnerStaticData } from './data/index.js'
|
|
16
|
-
import type { PackageType } from './index.js'
|
|
17
|
-
import { buildEbayListing } from './helpers/index.js'
|
|
18
|
-
|
|
19
|
-
import type {
|
|
20
|
-
EbayCategory,
|
|
21
|
-
EbayPolicy,
|
|
22
|
-
EcommerceCondition,
|
|
23
|
-
ExtendedEcommerceConditionMap,
|
|
24
|
-
EcommercePartnerConfiguration,
|
|
25
|
-
ExtendedEbayCategoryMap,
|
|
26
|
-
FileItem,
|
|
27
|
-
InventoryListingDetail,
|
|
28
|
-
InventoryRow,
|
|
29
|
-
InventoryType,
|
|
30
|
-
InventoryTypeListingDefaults,
|
|
31
|
-
NewInventoryListingDetail,
|
|
32
|
-
PolicyRowWithChecked,
|
|
33
|
-
} from './utils.js'
|
|
34
|
-
import { translate as defaultTranslate } from '@isoftdata/utility-string'
|
|
35
|
-
const { t: translate } = getContext<i18n>('i18next') || { t: defaultTranslate }
|
|
36
|
-
|
|
37
|
-
//
|
|
38
|
-
interface Props {
|
|
39
|
-
ebayCategoryList: Array<EbayCategory>
|
|
40
|
-
ebayCategoryMapList: Array<ExtendedEbayCategoryMap>
|
|
41
|
-
ebayPolicyList: Array<EbayPolicy>
|
|
42
|
-
ecommerceConditionMapList: Array<ExtendedEcommerceConditionMap>
|
|
43
|
-
ecommerceConditionList: Array<EcommerceCondition>
|
|
44
|
-
ecommercePartnerConfigurationList: Array<EcommercePartnerConfiguration>
|
|
45
|
-
listingList: Array<InventoryListingDetail>
|
|
46
|
-
partFileList: Array<FileItem>
|
|
47
|
-
part: InventoryRow
|
|
48
|
-
inventoryTypeWithDefaultConfigList: Array<InventoryTypeListingDefaults>
|
|
49
|
-
inventoryTypeList: Array<InventoryType>
|
|
50
|
-
save: (data: InventoryListingDetail | NewInventoryListingDetail) => Promise<void>
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
let {
|
|
54
|
-
ebayCategoryList,
|
|
55
|
-
ebayCategoryMapList,
|
|
56
|
-
ebayPolicyList,
|
|
57
|
-
ecommerceConditionMapList,
|
|
58
|
-
ecommerceConditionList,
|
|
59
|
-
ecommercePartnerConfigurationList,
|
|
60
|
-
listingList,
|
|
61
|
-
partFileList,
|
|
62
|
-
part,
|
|
63
|
-
inventoryTypeWithDefaultConfigList,
|
|
64
|
-
inventoryTypeList,
|
|
65
|
-
save = async (_: InventoryListingDetail | NewInventoryListingDetail) => {},
|
|
66
|
-
}: Props = $props()
|
|
67
|
-
|
|
68
|
-
let disableFields = $derived(!part.isWorldViewable)
|
|
69
|
-
let fulfillmentPolicyList = $derived.by(getFulfillmentPolicyList)
|
|
70
|
-
let lengthUnits = $derived.by(getPartnerLengthUnits)
|
|
71
|
-
let packageTypes = $derived.by(getPartnerPackageTypes)
|
|
72
|
-
let selectedEcommercePartnerId = $state(1)
|
|
73
|
-
let selectedPartnerConfig = $derived.by(getSelectedPartnerConfig)
|
|
74
|
-
let shippingFieldRequired = true // TODO: in the eBay world, this will need to be set if using calculated shipping but we don't know how we'll check that
|
|
75
|
-
let show = $state(false)
|
|
76
|
-
let showElement = $derived(selectedPartnerConfig?.name === 'ebay')
|
|
77
|
-
let uploadedListingImages = $derived.by(getListedImages)
|
|
78
|
-
let userPrompt: UserPrompt | undefined = $state(undefined)
|
|
79
|
-
let weightUnits = $derived.by(getPartnerWeightUnits)
|
|
80
|
-
|
|
81
|
-
// For images - moved before getOrCreateEcommerceListingObject to avoid initialization error
|
|
82
|
-
let newEcommerceListing = Object.freeze({
|
|
83
|
-
// Listing details
|
|
84
|
-
active: false,
|
|
85
|
-
//inventoryListingDetailId: null,
|
|
86
|
-
convertedListingDetails: null,
|
|
87
|
-
duration: 'GTC',
|
|
88
|
-
ecommercePartnerId: 0,
|
|
89
|
-
ecommerceCategoryId: null,
|
|
90
|
-
ecommerceConditionId: null,
|
|
91
|
-
ecommerceConditionDescription: null,
|
|
92
|
-
fulfillmentTime: null,
|
|
93
|
-
fulfillmentTimeUnit: null,
|
|
94
|
-
listingDescription: null,
|
|
95
|
-
listingStatus: 'pending',
|
|
96
|
-
listingTitle: null,
|
|
97
|
-
// Inventory specifics
|
|
98
|
-
imageUrls: [],
|
|
99
|
-
inventoryDescription: null,
|
|
100
|
-
inventoryId: 0,
|
|
101
|
-
lastUpdate: '',
|
|
102
|
-
manufacturerPartNumber: null,
|
|
103
|
-
message: [],
|
|
104
|
-
price: null,
|
|
105
|
-
quantity: null,
|
|
106
|
-
sku: null,
|
|
107
|
-
storeId: 0,
|
|
108
|
-
upc: '',
|
|
109
|
-
weight: null,
|
|
110
|
-
weightUnit: null,
|
|
111
|
-
shippingLength: null,
|
|
112
|
-
shippingWidth: null,
|
|
113
|
-
shippingHeight: null,
|
|
114
|
-
shippingLengthUnit: null,
|
|
115
|
-
shippingPackage: null,
|
|
116
|
-
// Partner specifics that don't fit into the other categories
|
|
117
|
-
partnerSpecificDetails: {
|
|
118
|
-
brand: null,
|
|
119
|
-
duration: 'GTC',
|
|
120
|
-
listingFormat: 'FIXED_PRICE',
|
|
121
|
-
fulfillmentPolicy: null,
|
|
122
|
-
marketplaceId: 'EBAY_MOTORS',
|
|
123
|
-
merchantLocationKey: null,
|
|
124
|
-
oemNumber: null,
|
|
125
|
-
paymentPolicy: null,
|
|
126
|
-
returnPolicy: null,
|
|
127
|
-
},
|
|
128
|
-
} as NewInventoryListingDetail)
|
|
129
|
-
|
|
130
|
-
let listing: InventoryListingDetail | NewInventoryListingDetail = $state(buildListingFromCurrentState()) // Had to explicitly set the type here
|
|
131
|
-
// New function to build listing using buildEbayListing helper
|
|
132
|
-
function buildListingFromCurrentState(): InventoryListingDetail | NewInventoryListingDetail {
|
|
133
|
-
// Find existing listing for the selected partner
|
|
134
|
-
const existingListing =
|
|
135
|
-
listingList.find(listing => listing.ecommercePartnerId === selectedEcommercePartnerId) || newEcommerceListing
|
|
136
|
-
|
|
137
|
-
// Find matching category mapping
|
|
138
|
-
const categoryMapping = ebayCategoryMapList.find(mapping => mapping.typeNum === part.inventoryTypeId)
|
|
139
|
-
|
|
140
|
-
// Find matching condition mapping
|
|
141
|
-
const conditionMapping = ecommerceConditionMapList.find(mapping => mapping.itrackCondition === part.condition)
|
|
142
|
-
|
|
143
|
-
// Find matching inventory type
|
|
144
|
-
const inventoryType = inventoryTypeList.find(type => type.inventoryTypeId === part.inventoryTypeId)
|
|
145
|
-
|
|
146
|
-
// Convert InventoryTypeListingDefaults to ExtendedInventoryTypeListingDefaults format
|
|
147
|
-
const extendedInventoryTypeListingDefaults = inventoryTypeWithDefaultConfigList.map(config => ({
|
|
148
|
-
...config,
|
|
149
|
-
categoryName: '', // This will need to be populated from actual category data
|
|
150
|
-
inventoryType: config.inventoryTypeId,
|
|
151
|
-
inventoryTypeName: inventoryType?.name || '',
|
|
152
|
-
}))
|
|
153
|
-
|
|
154
|
-
// Build the input for buildEbayListing
|
|
155
|
-
const buildInput = {
|
|
156
|
-
categoryMapping,
|
|
157
|
-
conditionMapping,
|
|
158
|
-
ecommercePartnerConfiguration: selectedPartnerConfig,
|
|
159
|
-
existingListing,
|
|
160
|
-
inventoryRow: part,
|
|
161
|
-
inventoryTypeListingDefaults: extendedInventoryTypeListingDefaults,
|
|
162
|
-
inventoryType,
|
|
163
|
-
partFileList,
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Call buildEbayListing and return the result, or fallback to newEcommerceListing
|
|
167
|
-
const result = buildEbayListing(buildInput)
|
|
168
|
-
return result || newEcommerceListing
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// This was originally a derived.by but then I got errors on the input binds
|
|
172
|
-
function getFulfillmentPolicyList(): Array<PolicyRowWithChecked> {
|
|
173
|
-
const policies = ebayPolicyList.filter(policy => policy.policyType === 'fulfillment')
|
|
174
|
-
const selectedPolicies = listing.partnerSpecificDetails.fulfillmentPolicy || []
|
|
175
|
-
return policies.map(policy => ({
|
|
176
|
-
...policy,
|
|
177
|
-
checked: selectedPolicies.includes(policy.policyId),
|
|
178
|
-
}))
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function getPaymentPolicyListWithChecked(): Array<PolicyRowWithChecked> {
|
|
182
|
-
const policies = ebayPolicyList?.filter(policy => policy.policyType === 'payment') || []
|
|
183
|
-
const selectedPolicies = listing.partnerSpecificDetails.paymentPolicy || []
|
|
184
|
-
return policies.map(policy => ({
|
|
185
|
-
...policy,
|
|
186
|
-
checked: selectedPolicies.includes(policy.policyId),
|
|
187
|
-
}))
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function getReturnPolicyListWithChecked(): Array<PolicyRowWithChecked> {
|
|
191
|
-
const policies = ebayPolicyList?.filter(policy => policy.policyType === 'return') || []
|
|
192
|
-
const selectedPolicies = listing.partnerSpecificDetails.returnPolicy || []
|
|
193
|
-
return policies.map(policy => ({
|
|
194
|
-
...policy,
|
|
195
|
-
checked: selectedPolicies.includes(policy.policyId),
|
|
196
|
-
}))
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function handleFulfillmentPoliciesChange(selectedIds: Array<string>) {
|
|
200
|
-
listing.partnerSpecificDetails.fulfillmentPolicy = selectedIds
|
|
201
|
-
handleSave()
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function handlePaymentPoliciesChange(selectedIds: Array<string>) {
|
|
205
|
-
listing.partnerSpecificDetails.paymentPolicy = selectedIds
|
|
206
|
-
handleSave()
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function handleReturnPoliciesChange(selectedIds: Array<string>) {
|
|
210
|
-
listing.partnerSpecificDetails.returnPolicy = selectedIds
|
|
211
|
-
handleSave()
|
|
212
|
-
}
|
|
213
|
-
// Helper that returns any images that have successfully uploaded to eBay to show on Ecommerce page
|
|
214
|
-
function getListedImages() {
|
|
215
|
-
// Early return if we don't have the necessary data
|
|
216
|
-
if (!partFileList.length || !listingList.length) {
|
|
217
|
-
return []
|
|
218
|
-
}
|
|
219
|
-
// See if there's an existing listing row (should be outside of a race condition where part's still saving when they switched tabs)
|
|
220
|
-
const existingInventoryListingDetailRow = listingList.find(
|
|
221
|
-
listing => listing.ecommercePartnerId === selectedEcommercePartnerId,
|
|
222
|
-
)
|
|
223
|
-
const uploadedImages = existingInventoryListingDetailRow?.convertedListingDetails?.imageUrls
|
|
224
|
-
if (uploadedImages?.length) {
|
|
225
|
-
const matchingFilesWithUrl = partFileList.filter(partFile => {
|
|
226
|
-
const matchingUploadedUrl = uploadedImages.find(image => image.fileId === partFile.fileId)
|
|
227
|
-
return matchingUploadedUrl !== undefined
|
|
228
|
-
})
|
|
229
|
-
return matchingFilesWithUrl.map(file => ({
|
|
230
|
-
...file,
|
|
231
|
-
uuid: uuid(),
|
|
232
|
-
}))
|
|
233
|
-
}
|
|
234
|
-
return []
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function getPartnerPackageTypes(): Array<PackageType> | [] {
|
|
238
|
-
const partnerName = selectedPartnerConfig?.name?.toLowerCase()
|
|
239
|
-
if (!partnerName || !(partnerName in ecommercePartnerStaticData)) {
|
|
240
|
-
return []
|
|
241
|
-
}
|
|
242
|
-
return ecommercePartnerStaticData[partnerName as keyof typeof ecommercePartnerStaticData].packageTypes
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function getPartnerWeightUnits(): Array<string> {
|
|
246
|
-
const partnerName = selectedPartnerConfig?.name?.toLowerCase()
|
|
247
|
-
if (!partnerName || !(partnerName in ecommercePartnerStaticData)) {
|
|
248
|
-
return []
|
|
249
|
-
}
|
|
250
|
-
return ecommercePartnerStaticData[partnerName as keyof typeof ecommercePartnerStaticData].weightUnits
|
|
251
|
-
}
|
|
252
|
-
function getPartnerLengthUnits(): Array<string> {
|
|
253
|
-
const partnerName = selectedPartnerConfig?.name?.toLowerCase()
|
|
254
|
-
if (!partnerName || !(partnerName in ecommercePartnerStaticData)) {
|
|
255
|
-
return []
|
|
256
|
-
}
|
|
257
|
-
return ecommercePartnerStaticData[partnerName as keyof typeof ecommercePartnerStaticData].lengthUnits
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// TODO: This is a temporary function to get the selected partner config, probably not needed in derived
|
|
261
|
-
function getSelectedPartnerConfig(): EcommercePartnerConfiguration {
|
|
262
|
-
const matchingPartnerConfig = ecommercePartnerConfigurationList.find(
|
|
263
|
-
row => row.ecommercePartnerId === selectedEcommercePartnerId,
|
|
264
|
-
)
|
|
265
|
-
if (!matchingPartnerConfig) {
|
|
266
|
-
throw new Error('No matching partner config found')
|
|
267
|
-
}
|
|
268
|
-
// Ensure defaults property exists
|
|
269
|
-
if (!matchingPartnerConfig.defaults) {
|
|
270
|
-
matchingPartnerConfig.defaults = {
|
|
271
|
-
global: {
|
|
272
|
-
listingDescriptionTemplate: '',
|
|
273
|
-
listingDuration: 'GTC',
|
|
274
|
-
listingFormat: 'FIXED_PRICE',
|
|
275
|
-
listingTitleTemplate: '',
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
if
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
return allMessages
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
return
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
//
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
class
|
|
418
|
-
class:badge-
|
|
419
|
-
class:badge-
|
|
420
|
-
|
|
421
|
-
{listing.listingStatus}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
</div>
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
{
|
|
490
|
-
<p>{translate('ecommerce.configuration.
|
|
491
|
-
{:else}
|
|
492
|
-
<
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
</div>
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
{
|
|
592
|
-
|
|
593
|
-
{
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
{
|
|
641
|
-
|
|
642
|
-
{
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
{
|
|
684
|
-
|
|
685
|
-
{
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
{
|
|
698
|
-
|
|
699
|
-
{
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
class
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
{
|
|
752
|
-
|
|
753
|
-
{
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
{
|
|
775
|
-
|
|
776
|
-
{
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
</div>
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
</
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
{
|
|
850
|
-
|
|
851
|
-
{
|
|
852
|
-
</
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { i18n } from 'i18next'
|
|
3
|
+
import { getContext } from 'svelte'
|
|
4
|
+
|
|
5
|
+
import Button from '@isoftdata/svelte-button'
|
|
6
|
+
import CurrencyInput from '@isoftdata/svelte-currency-input'
|
|
7
|
+
import Input from '@isoftdata/svelte-input'
|
|
8
|
+
import Modal from '@isoftdata/svelte-modal'
|
|
9
|
+
import Select from '@isoftdata/svelte-select'
|
|
10
|
+
import Textarea from '@isoftdata/svelte-textarea'
|
|
11
|
+
import PolicyList from './PolicyList.svelte'
|
|
12
|
+
import { ThumbnailGrid } from '@isoftdata/svelte-attachments'
|
|
13
|
+
import UserPrompt from '@isoftdata/svelte-user-prompt'
|
|
14
|
+
import { v4 as uuid } from '@lukeed/uuid'
|
|
15
|
+
import { ecommercePartnerStaticData } from './data/index.js'
|
|
16
|
+
import type { PackageType } from './index.js'
|
|
17
|
+
import { buildEbayListing } from './helpers/index.js'
|
|
18
|
+
|
|
19
|
+
import type {
|
|
20
|
+
EbayCategory,
|
|
21
|
+
EbayPolicy,
|
|
22
|
+
EcommerceCondition,
|
|
23
|
+
ExtendedEcommerceConditionMap,
|
|
24
|
+
EcommercePartnerConfiguration,
|
|
25
|
+
ExtendedEbayCategoryMap,
|
|
26
|
+
FileItem,
|
|
27
|
+
InventoryListingDetail,
|
|
28
|
+
InventoryRow,
|
|
29
|
+
InventoryType,
|
|
30
|
+
InventoryTypeListingDefaults,
|
|
31
|
+
NewInventoryListingDetail,
|
|
32
|
+
PolicyRowWithChecked,
|
|
33
|
+
} from './utils.js'
|
|
34
|
+
import { translate as defaultTranslate } from '@isoftdata/utility-string'
|
|
35
|
+
const { t: translate } = getContext<i18n>('i18next') || { t: defaultTranslate }
|
|
36
|
+
|
|
37
|
+
//
|
|
38
|
+
interface Props {
|
|
39
|
+
ebayCategoryList: Array<EbayCategory>
|
|
40
|
+
ebayCategoryMapList: Array<ExtendedEbayCategoryMap>
|
|
41
|
+
ebayPolicyList: Array<EbayPolicy>
|
|
42
|
+
ecommerceConditionMapList: Array<ExtendedEcommerceConditionMap>
|
|
43
|
+
ecommerceConditionList: Array<EcommerceCondition>
|
|
44
|
+
ecommercePartnerConfigurationList: Array<EcommercePartnerConfiguration>
|
|
45
|
+
listingList: Array<InventoryListingDetail>
|
|
46
|
+
partFileList: Array<FileItem>
|
|
47
|
+
part: InventoryRow
|
|
48
|
+
inventoryTypeWithDefaultConfigList: Array<InventoryTypeListingDefaults>
|
|
49
|
+
inventoryTypeList: Array<InventoryType>
|
|
50
|
+
save: (data: InventoryListingDetail | NewInventoryListingDetail) => Promise<void>
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let {
|
|
54
|
+
ebayCategoryList,
|
|
55
|
+
ebayCategoryMapList,
|
|
56
|
+
ebayPolicyList,
|
|
57
|
+
ecommerceConditionMapList,
|
|
58
|
+
ecommerceConditionList,
|
|
59
|
+
ecommercePartnerConfigurationList,
|
|
60
|
+
listingList,
|
|
61
|
+
partFileList,
|
|
62
|
+
part,
|
|
63
|
+
inventoryTypeWithDefaultConfigList,
|
|
64
|
+
inventoryTypeList,
|
|
65
|
+
save = async (_: InventoryListingDetail | NewInventoryListingDetail) => {},
|
|
66
|
+
}: Props = $props()
|
|
67
|
+
|
|
68
|
+
let disableFields = $derived(!part.isWorldViewable)
|
|
69
|
+
let fulfillmentPolicyList = $derived.by(getFulfillmentPolicyList)
|
|
70
|
+
let lengthUnits = $derived.by(getPartnerLengthUnits)
|
|
71
|
+
let packageTypes = $derived.by(getPartnerPackageTypes)
|
|
72
|
+
let selectedEcommercePartnerId = $state(1)
|
|
73
|
+
let selectedPartnerConfig = $derived.by(getSelectedPartnerConfig)
|
|
74
|
+
let shippingFieldRequired = true // TODO: in the eBay world, this will need to be set if using calculated shipping but we don't know how we'll check that
|
|
75
|
+
let show = $state(false)
|
|
76
|
+
let showElement = $derived(selectedPartnerConfig?.name === 'ebay')
|
|
77
|
+
let uploadedListingImages = $derived.by(getListedImages)
|
|
78
|
+
let userPrompt: UserPrompt | undefined = $state(undefined)
|
|
79
|
+
let weightUnits = $derived.by(getPartnerWeightUnits)
|
|
80
|
+
|
|
81
|
+
// For images - moved before getOrCreateEcommerceListingObject to avoid initialization error
|
|
82
|
+
let newEcommerceListing = Object.freeze({
|
|
83
|
+
// Listing details
|
|
84
|
+
active: false,
|
|
85
|
+
//inventoryListingDetailId: null,
|
|
86
|
+
convertedListingDetails: null,
|
|
87
|
+
duration: 'GTC',
|
|
88
|
+
ecommercePartnerId: 0,
|
|
89
|
+
ecommerceCategoryId: null,
|
|
90
|
+
ecommerceConditionId: null,
|
|
91
|
+
ecommerceConditionDescription: null,
|
|
92
|
+
fulfillmentTime: null,
|
|
93
|
+
fulfillmentTimeUnit: null,
|
|
94
|
+
listingDescription: null,
|
|
95
|
+
listingStatus: 'pending',
|
|
96
|
+
listingTitle: null,
|
|
97
|
+
// Inventory specifics
|
|
98
|
+
imageUrls: [],
|
|
99
|
+
inventoryDescription: null,
|
|
100
|
+
inventoryId: 0,
|
|
101
|
+
lastUpdate: '',
|
|
102
|
+
manufacturerPartNumber: null,
|
|
103
|
+
message: [],
|
|
104
|
+
price: null,
|
|
105
|
+
quantity: null,
|
|
106
|
+
sku: null,
|
|
107
|
+
storeId: 0,
|
|
108
|
+
upc: '',
|
|
109
|
+
weight: null,
|
|
110
|
+
weightUnit: null,
|
|
111
|
+
shippingLength: null,
|
|
112
|
+
shippingWidth: null,
|
|
113
|
+
shippingHeight: null,
|
|
114
|
+
shippingLengthUnit: null,
|
|
115
|
+
shippingPackage: null,
|
|
116
|
+
// Partner specifics that don't fit into the other categories
|
|
117
|
+
partnerSpecificDetails: {
|
|
118
|
+
brand: null,
|
|
119
|
+
duration: 'GTC',
|
|
120
|
+
listingFormat: 'FIXED_PRICE',
|
|
121
|
+
fulfillmentPolicy: null,
|
|
122
|
+
marketplaceId: 'EBAY_MOTORS',
|
|
123
|
+
merchantLocationKey: null,
|
|
124
|
+
oemNumber: null,
|
|
125
|
+
paymentPolicy: null,
|
|
126
|
+
returnPolicy: null,
|
|
127
|
+
},
|
|
128
|
+
} as NewInventoryListingDetail)
|
|
129
|
+
|
|
130
|
+
let listing: InventoryListingDetail | NewInventoryListingDetail = $state(buildListingFromCurrentState()) // Had to explicitly set the type here
|
|
131
|
+
// New function to build listing using buildEbayListing helper
|
|
132
|
+
function buildListingFromCurrentState(): InventoryListingDetail | NewInventoryListingDetail {
|
|
133
|
+
// Find existing listing for the selected partner
|
|
134
|
+
const existingListing =
|
|
135
|
+
listingList.find(listing => listing.ecommercePartnerId === selectedEcommercePartnerId) || newEcommerceListing
|
|
136
|
+
|
|
137
|
+
// Find matching category mapping
|
|
138
|
+
const categoryMapping = ebayCategoryMapList.find(mapping => mapping.typeNum === part.inventoryTypeId)
|
|
139
|
+
|
|
140
|
+
// Find matching condition mapping
|
|
141
|
+
const conditionMapping = ecommerceConditionMapList.find(mapping => mapping.itrackCondition === part.condition)
|
|
142
|
+
|
|
143
|
+
// Find matching inventory type
|
|
144
|
+
const inventoryType = inventoryTypeList.find(type => type.inventoryTypeId === part.inventoryTypeId)
|
|
145
|
+
|
|
146
|
+
// Convert InventoryTypeListingDefaults to ExtendedInventoryTypeListingDefaults format
|
|
147
|
+
const extendedInventoryTypeListingDefaults = inventoryTypeWithDefaultConfigList.map(config => ({
|
|
148
|
+
...config,
|
|
149
|
+
categoryName: '', // This will need to be populated from actual category data
|
|
150
|
+
inventoryType: config.inventoryTypeId,
|
|
151
|
+
inventoryTypeName: inventoryType?.name || '',
|
|
152
|
+
}))
|
|
153
|
+
|
|
154
|
+
// Build the input for buildEbayListing
|
|
155
|
+
const buildInput = {
|
|
156
|
+
categoryMapping,
|
|
157
|
+
conditionMapping,
|
|
158
|
+
ecommercePartnerConfiguration: selectedPartnerConfig,
|
|
159
|
+
existingListing,
|
|
160
|
+
inventoryRow: part,
|
|
161
|
+
inventoryTypeListingDefaults: extendedInventoryTypeListingDefaults,
|
|
162
|
+
inventoryType,
|
|
163
|
+
partFileList,
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Call buildEbayListing and return the result, or fallback to newEcommerceListing
|
|
167
|
+
const result = buildEbayListing(buildInput)
|
|
168
|
+
return result || newEcommerceListing
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// This was originally a derived.by but then I got errors on the input binds
|
|
172
|
+
function getFulfillmentPolicyList(): Array<PolicyRowWithChecked> {
|
|
173
|
+
const policies = ebayPolicyList.filter(policy => policy.policyType === 'fulfillment')
|
|
174
|
+
const selectedPolicies = listing.partnerSpecificDetails.fulfillmentPolicy || []
|
|
175
|
+
return policies.map(policy => ({
|
|
176
|
+
...policy,
|
|
177
|
+
checked: selectedPolicies.includes(policy.policyId),
|
|
178
|
+
}))
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function getPaymentPolicyListWithChecked(): Array<PolicyRowWithChecked> {
|
|
182
|
+
const policies = ebayPolicyList?.filter(policy => policy.policyType === 'payment') || []
|
|
183
|
+
const selectedPolicies = listing.partnerSpecificDetails.paymentPolicy || []
|
|
184
|
+
return policies.map(policy => ({
|
|
185
|
+
...policy,
|
|
186
|
+
checked: selectedPolicies.includes(policy.policyId),
|
|
187
|
+
}))
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function getReturnPolicyListWithChecked(): Array<PolicyRowWithChecked> {
|
|
191
|
+
const policies = ebayPolicyList?.filter(policy => policy.policyType === 'return') || []
|
|
192
|
+
const selectedPolicies = listing.partnerSpecificDetails.returnPolicy || []
|
|
193
|
+
return policies.map(policy => ({
|
|
194
|
+
...policy,
|
|
195
|
+
checked: selectedPolicies.includes(policy.policyId),
|
|
196
|
+
}))
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function handleFulfillmentPoliciesChange(selectedIds: Array<string>) {
|
|
200
|
+
listing.partnerSpecificDetails.fulfillmentPolicy = selectedIds
|
|
201
|
+
handleSave()
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function handlePaymentPoliciesChange(selectedIds: Array<string>) {
|
|
205
|
+
listing.partnerSpecificDetails.paymentPolicy = selectedIds
|
|
206
|
+
handleSave()
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function handleReturnPoliciesChange(selectedIds: Array<string>) {
|
|
210
|
+
listing.partnerSpecificDetails.returnPolicy = selectedIds
|
|
211
|
+
handleSave()
|
|
212
|
+
}
|
|
213
|
+
// Helper that returns any images that have successfully uploaded to eBay to show on Ecommerce page
|
|
214
|
+
function getListedImages() {
|
|
215
|
+
// Early return if we don't have the necessary data
|
|
216
|
+
if (!partFileList.length || !listingList.length) {
|
|
217
|
+
return []
|
|
218
|
+
}
|
|
219
|
+
// See if there's an existing listing row (should be outside of a race condition where part's still saving when they switched tabs)
|
|
220
|
+
const existingInventoryListingDetailRow = listingList.find(
|
|
221
|
+
listing => listing.ecommercePartnerId === selectedEcommercePartnerId,
|
|
222
|
+
)
|
|
223
|
+
const uploadedImages = existingInventoryListingDetailRow?.convertedListingDetails?.imageUrls
|
|
224
|
+
if (uploadedImages?.length) {
|
|
225
|
+
const matchingFilesWithUrl = partFileList.filter(partFile => {
|
|
226
|
+
const matchingUploadedUrl = uploadedImages.find(image => image.fileId === partFile.fileId)
|
|
227
|
+
return matchingUploadedUrl !== undefined
|
|
228
|
+
})
|
|
229
|
+
return matchingFilesWithUrl.map(file => ({
|
|
230
|
+
...file,
|
|
231
|
+
uuid: uuid(),
|
|
232
|
+
}))
|
|
233
|
+
}
|
|
234
|
+
return []
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function getPartnerPackageTypes(): Array<PackageType> | [] {
|
|
238
|
+
const partnerName = selectedPartnerConfig?.name?.toLowerCase()
|
|
239
|
+
if (!partnerName || !(partnerName in ecommercePartnerStaticData)) {
|
|
240
|
+
return []
|
|
241
|
+
}
|
|
242
|
+
return ecommercePartnerStaticData[partnerName as keyof typeof ecommercePartnerStaticData].packageTypes
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function getPartnerWeightUnits(): Array<string> {
|
|
246
|
+
const partnerName = selectedPartnerConfig?.name?.toLowerCase()
|
|
247
|
+
if (!partnerName || !(partnerName in ecommercePartnerStaticData)) {
|
|
248
|
+
return []
|
|
249
|
+
}
|
|
250
|
+
return ecommercePartnerStaticData[partnerName as keyof typeof ecommercePartnerStaticData].weightUnits
|
|
251
|
+
}
|
|
252
|
+
function getPartnerLengthUnits(): Array<string> {
|
|
253
|
+
const partnerName = selectedPartnerConfig?.name?.toLowerCase()
|
|
254
|
+
if (!partnerName || !(partnerName in ecommercePartnerStaticData)) {
|
|
255
|
+
return []
|
|
256
|
+
}
|
|
257
|
+
return ecommercePartnerStaticData[partnerName as keyof typeof ecommercePartnerStaticData].lengthUnits
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// TODO: This is a temporary function to get the selected partner config, probably not needed in derived
|
|
261
|
+
function getSelectedPartnerConfig(): EcommercePartnerConfiguration {
|
|
262
|
+
const matchingPartnerConfig = ecommercePartnerConfigurationList.find(
|
|
263
|
+
row => row.ecommercePartnerId === selectedEcommercePartnerId,
|
|
264
|
+
)
|
|
265
|
+
if (!matchingPartnerConfig) {
|
|
266
|
+
throw new Error('No matching partner config found')
|
|
267
|
+
}
|
|
268
|
+
// Ensure defaults property exists
|
|
269
|
+
if (!matchingPartnerConfig.defaults) {
|
|
270
|
+
matchingPartnerConfig.defaults = {
|
|
271
|
+
global: {
|
|
272
|
+
listingDescriptionTemplate: '',
|
|
273
|
+
listingDuration: 'GTC',
|
|
274
|
+
listingFormat: 'FIXED_PRICE',
|
|
275
|
+
listingTitleTemplate: '',
|
|
276
|
+
pricingModifier: 100,
|
|
277
|
+
defaultPriceType: ''
|
|
278
|
+
},
|
|
279
|
+
store: [],
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return matchingPartnerConfig
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function getMessageType(messageObject: any): string {
|
|
286
|
+
if (!messageObject) {
|
|
287
|
+
return ''
|
|
288
|
+
}
|
|
289
|
+
// Try to parse if it's a JSON string
|
|
290
|
+
let parsedObject = messageObject
|
|
291
|
+
if (typeof messageObject === 'string') {
|
|
292
|
+
try {
|
|
293
|
+
parsedObject = JSON.parse(messageObject)
|
|
294
|
+
} catch {
|
|
295
|
+
// If parsing fails but there's data, return 'text'
|
|
296
|
+
return messageObject.trim() ? 'text' : 'empty'
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// If it's an array, get the first object and its first property value
|
|
301
|
+
if (Array.isArray(parsedObject)) {
|
|
302
|
+
if (parsedObject.length > 0) {
|
|
303
|
+
const firstItem = parsedObject[0]
|
|
304
|
+
if (typeof firstItem === 'object' && firstItem !== null) {
|
|
305
|
+
const keys = Object.keys(firstItem)
|
|
306
|
+
if (keys.length > 0) {
|
|
307
|
+
const firstKey = keys[0]
|
|
308
|
+
return firstItem[firstKey]
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// If first item isn't an object, return 'array'
|
|
312
|
+
return parsedObject[0]
|
|
313
|
+
}
|
|
314
|
+
return ''
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// If it's an object, get the first property value
|
|
318
|
+
if (typeof parsedObject === 'object' && parsedObject !== null) {
|
|
319
|
+
const keys = Object.keys(parsedObject)
|
|
320
|
+
if (keys.length > 0) {
|
|
321
|
+
const firstKey = keys[0]
|
|
322
|
+
return parsedObject[firstKey]
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Fallback for any other case with data
|
|
327
|
+
return 'unknown'
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function formatMessageForDisplay(messageObject: any): Array<string> {
|
|
331
|
+
if (!messageObject) {
|
|
332
|
+
return ['No message available']
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// If it's an array, extract messages from each item
|
|
336
|
+
if (Array.isArray(messageObject)) {
|
|
337
|
+
if (messageObject.length === 0) {
|
|
338
|
+
return ['No messages']
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const allMessages: Array<string> = []
|
|
342
|
+
|
|
343
|
+
messageObject.forEach(item => {
|
|
344
|
+
if (typeof item === 'object' && item !== null) {
|
|
345
|
+
// Check if it has a 'messages' property (array of actual messages)
|
|
346
|
+
if (item.messages && Array.isArray(item.messages)) {
|
|
347
|
+
allMessages.push(...item.messages)
|
|
348
|
+
} else if (item.message) {
|
|
349
|
+
// Single message property
|
|
350
|
+
allMessages.push(item.message)
|
|
351
|
+
} else {
|
|
352
|
+
// Fallback to string representation
|
|
353
|
+
allMessages.push(JSON.stringify(item))
|
|
354
|
+
}
|
|
355
|
+
} else {
|
|
356
|
+
allMessages.push(String(item))
|
|
357
|
+
}
|
|
358
|
+
})
|
|
359
|
+
//return allMessages
|
|
360
|
+
//return allMessages.join('\n---\n')
|
|
361
|
+
return allMessages
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// If it's an object, check for message properties
|
|
365
|
+
if (typeof messageObject === 'object' && messageObject !== null) {
|
|
366
|
+
if (messageObject.messages && Array.isArray(messageObject.messages)) {
|
|
367
|
+
return messageObject.messages.join('\n')
|
|
368
|
+
} else {
|
|
369
|
+
return [JSON.stringify(messageObject, null, 2)]
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Fallback for any other type
|
|
374
|
+
return [String(messageObject)]
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Handle clicking on listing ID to open external link
|
|
378
|
+
// TODO: this isn't very friendly to other partners
|
|
379
|
+
function handleListingIdClick(listing: InventoryListingDetail | NewInventoryListingDetail) {
|
|
380
|
+
// TODO: Implement logic to construct the appropriate URL based on partner
|
|
381
|
+
// For eBay, this might be something like: https://www.ebay.com/itm/{listingId}
|
|
382
|
+
const listingId = listing.convertedListingDetails?.listingId
|
|
383
|
+
if (selectedPartnerConfig?.name?.toLowerCase() === 'ebay' && listingId) {
|
|
384
|
+
// For eBay, open the listing page (this is a placeholder URL structure)
|
|
385
|
+
const production = false // TODO: figure out how/where we decide to set this and how to make certain components aware of it
|
|
386
|
+
// The api call for get item returns the following format for sandbox https://sandbox.ebay.com/itm/${sku}-/${listingId}
|
|
387
|
+
const ebayUrl = production ? `https://ebay.com/itm/${listingId}` : `https://sandbox.ebay.com/itm/${listingId}`
|
|
388
|
+
window.open(ebayUrl, '_blank')
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async function handleSave() {
|
|
393
|
+
// If required fields are filled out, set a flag so Agg can pick it up and process it
|
|
394
|
+
if (!listing) return
|
|
395
|
+
await save(listing)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Taken from Basic.svelte
|
|
399
|
+
function zeroIfNull(value: number | null) {
|
|
400
|
+
return value ?? 0
|
|
401
|
+
}
|
|
402
|
+
</script>
|
|
403
|
+
|
|
404
|
+
<div class="form-row">
|
|
405
|
+
<div class="col-lg d-flex flex-wrap">
|
|
406
|
+
<div class="card hightlight-card mt-4 w-100">
|
|
407
|
+
<div class="card-header">{translate('ecommerce.configuration.listingStatus', 'Listing Status')}</div>
|
|
408
|
+
{#if listing}
|
|
409
|
+
<div class="card-body">
|
|
410
|
+
<div class="d-flex flex-wrap align-items-center mb-2">
|
|
411
|
+
<div
|
|
412
|
+
class="d-flex align-items-center"
|
|
413
|
+
style="margin-right: 2rem;"
|
|
414
|
+
>
|
|
415
|
+
<strong style="margin-right: 0.5rem;">{translate('ecommerce.configuration.status', 'Status')}:</strong>
|
|
416
|
+
<span
|
|
417
|
+
class="badge"
|
|
418
|
+
class:badge-danger={listing.listingStatus === 'error'}
|
|
419
|
+
class:badge-warn={listing.listingStatus === 'cancelled'}
|
|
420
|
+
class:badge-primary={listing.listingStatus === 'pending'}
|
|
421
|
+
class:badge-success={listing.listingStatus === 'listed'}
|
|
422
|
+
>
|
|
423
|
+
{listing.listingStatus}
|
|
424
|
+
</span>
|
|
425
|
+
</div>
|
|
426
|
+
|
|
427
|
+
<div
|
|
428
|
+
class="d-flex align-items-center"
|
|
429
|
+
style="margin-right: 2rem;"
|
|
430
|
+
>
|
|
431
|
+
<strong style="margin-right: 0.5rem;"
|
|
432
|
+
>{translate('ecommerce.configuration.lastUpdate', 'Last Update')}:</strong
|
|
433
|
+
>
|
|
434
|
+
<span>{listing.lastUpdate ? new Date(listing.lastUpdate).toLocaleString() : 'N/A'}</span>
|
|
435
|
+
</div>
|
|
436
|
+
|
|
437
|
+
{#if listing?.convertedListingDetails?.listingId}
|
|
438
|
+
<div class="d-flex align-items-center">
|
|
439
|
+
<Button
|
|
440
|
+
outline
|
|
441
|
+
size="sm"
|
|
442
|
+
onclick={() => handleListingIdClick(listing)}
|
|
443
|
+
>{translate('ecommerce.configuration.viewOnEbay', 'View On eBay')}</Button
|
|
444
|
+
>
|
|
445
|
+
</div>
|
|
446
|
+
{/if}
|
|
447
|
+
{#if listing.message}
|
|
448
|
+
<div class="d-flex align-items-center">
|
|
449
|
+
<strong style="margin-right: 0.5rem;">{translate('ecommerce.configuration.message', 'Message')}:</strong
|
|
450
|
+
>
|
|
451
|
+
|
|
452
|
+
<span>{getMessageType(listing.message)}</span>
|
|
453
|
+
</div>
|
|
454
|
+
{/if}
|
|
455
|
+
{#if listing.listingStatus === 'error'}
|
|
456
|
+
<div
|
|
457
|
+
class="d-flex align-items-center"
|
|
458
|
+
style="margin-left: 2rem;"
|
|
459
|
+
>
|
|
460
|
+
<Button
|
|
461
|
+
outline
|
|
462
|
+
style="margin-right: 0.5rem;"
|
|
463
|
+
color="success"
|
|
464
|
+
size="sm"
|
|
465
|
+
iconClass="search"
|
|
466
|
+
onclick={() => {
|
|
467
|
+
show = true
|
|
468
|
+
}}
|
|
469
|
+
>
|
|
470
|
+
{translate('ecommerce.configuration.viewErrors', 'View Errors')}...
|
|
471
|
+
</Button>
|
|
472
|
+
</div>
|
|
473
|
+
{/if}
|
|
474
|
+
</div>
|
|
475
|
+
</div>
|
|
476
|
+
{/if}
|
|
477
|
+
</div>
|
|
478
|
+
</div>
|
|
479
|
+
</div>
|
|
480
|
+
<div class="form-row">
|
|
481
|
+
<div class="col-lg d-flex flex-wrap">
|
|
482
|
+
<div class="card hightlight-card mt-4 w-100">
|
|
483
|
+
<div class="card-header">
|
|
484
|
+
{translate('ecommerce.configuration.uploadedListingImages', 'Uploaded Listing Images')}
|
|
485
|
+
</div>
|
|
486
|
+
<div class="card-body">
|
|
487
|
+
<div class="form-row">
|
|
488
|
+
<div class="col-md">
|
|
489
|
+
{#if !listing?.imageUrls?.length && !uploadedListingImages.length}
|
|
490
|
+
<p>{translate('ecommerce.configuration.noAttachmentsFound', 'No attachments found for this part')}</p>
|
|
491
|
+
{:else if listing?.imageUrls?.length && !uploadedListingImages.length && listing.listingStatus === 'pending'}
|
|
492
|
+
<p>{translate('ecommerce.configuration.waitingEbaySync', 'Images waiting for eBay sync')}</p>
|
|
493
|
+
{:else}
|
|
494
|
+
<ThumbnailGrid
|
|
495
|
+
fileList={uploadedListingImages}
|
|
496
|
+
modificationDisabled
|
|
497
|
+
hidePublicFeatures
|
|
498
|
+
hideRankFeatures
|
|
499
|
+
/>
|
|
500
|
+
{/if}
|
|
501
|
+
</div>
|
|
502
|
+
</div>
|
|
503
|
+
</div>
|
|
504
|
+
</div>
|
|
505
|
+
</div>
|
|
506
|
+
</div>
|
|
507
|
+
<div class="form-row">
|
|
508
|
+
<div class="col-lg d-flex flex-wrap">
|
|
509
|
+
<div class="card hightlight-card mt-4 w-100">
|
|
510
|
+
<div class="card-header">{translate('ecommerce.configuration.partDetails', 'Part Details')}</div>
|
|
511
|
+
<div class="card-body">
|
|
512
|
+
<div class="form-row">
|
|
513
|
+
<div class="col-md">
|
|
514
|
+
<div class="form-row">
|
|
515
|
+
<div class="col">
|
|
516
|
+
<Input
|
|
517
|
+
type="text"
|
|
518
|
+
label={translate('ecommerce.configuration.ebaySku', 'Ebay SKU')}
|
|
519
|
+
disabled={true}
|
|
520
|
+
required={true}
|
|
521
|
+
value={listing.sku}
|
|
522
|
+
/>
|
|
523
|
+
</div>
|
|
524
|
+
<div class="col">
|
|
525
|
+
<Input
|
|
526
|
+
type="text"
|
|
527
|
+
label={translate('ecommerce.configuration.store', 'Store')}
|
|
528
|
+
disabled={true}
|
|
529
|
+
required={true}
|
|
530
|
+
value={listing.storeId}
|
|
531
|
+
/>
|
|
532
|
+
</div>
|
|
533
|
+
<div class="col">
|
|
534
|
+
<Input
|
|
535
|
+
inputmode="decimal"
|
|
536
|
+
type="number"
|
|
537
|
+
class={zeroIfNull(part.quantity) > zeroIfNull(part.maximumQuantity) ||
|
|
538
|
+
zeroIfNull(part.quantity) < zeroIfNull(part.minimumQuantity)
|
|
539
|
+
? 'border-danger'
|
|
540
|
+
: ''}
|
|
541
|
+
label={translate('ecommerce.configuration.quantity', 'Quantity')}
|
|
542
|
+
hint={translate('ecommerce.configuration.onHand', 'On Hand')}
|
|
543
|
+
disabled={true}
|
|
544
|
+
required={true}
|
|
545
|
+
value={listing.quantity}
|
|
546
|
+
></Input>
|
|
547
|
+
</div>
|
|
548
|
+
<div class="col">
|
|
549
|
+
<CurrencyInput
|
|
550
|
+
label={translate('ecommerce.configuration.price', 'Price')}
|
|
551
|
+
name="retailprice"
|
|
552
|
+
disabled={true}
|
|
553
|
+
required={true}
|
|
554
|
+
class={(listing.price ?? 0) === 0 ? 'is-invalid' : ''}
|
|
555
|
+
value={(listing.price ?? 0).toString()}
|
|
556
|
+
/>
|
|
557
|
+
</div>
|
|
558
|
+
</div>
|
|
559
|
+
<div class="form-row">
|
|
560
|
+
<div class="col">
|
|
561
|
+
<Input
|
|
562
|
+
type="text"
|
|
563
|
+
label={translate('ecommerce.configuration.brand', 'Brand')}
|
|
564
|
+
disabled={disableFields}
|
|
565
|
+
bind:value={listing.partnerSpecificDetails.brand}
|
|
566
|
+
/>
|
|
567
|
+
</div>
|
|
568
|
+
<div class="col">
|
|
569
|
+
<Input
|
|
570
|
+
type="text"
|
|
571
|
+
label={translate('ecommerce.configuration.manufPartNumber', 'Manuf. Part Number')}
|
|
572
|
+
disabled={disableFields}
|
|
573
|
+
bind:value={listing.manufacturerPartNumber}
|
|
574
|
+
/>
|
|
575
|
+
</div>
|
|
576
|
+
<div class="col">
|
|
577
|
+
<Input
|
|
578
|
+
type="text"
|
|
579
|
+
label={translate('ecommerce.configuration.upc', 'UPC')}
|
|
580
|
+
disabled={disableFields}
|
|
581
|
+
bind:value={listing.upc}
|
|
582
|
+
/>
|
|
583
|
+
</div>
|
|
584
|
+
</div>
|
|
585
|
+
<div class="form-row">
|
|
586
|
+
<div class="col">
|
|
587
|
+
<Select
|
|
588
|
+
label={translate('ecommerce.configuration.condition', 'Condition')}
|
|
589
|
+
emptyText="-- {translate('ecommerce.configuration.selectCondition', 'Select Condition')} --"
|
|
590
|
+
required={true}
|
|
591
|
+
bind:value={listing.ecommerceConditionId}
|
|
592
|
+
>
|
|
593
|
+
{#each ecommerceConditionList as ecommerceCondition}
|
|
594
|
+
<option value={ecommerceCondition.ecommerceConditionId}>{ecommerceCondition.name}</option>
|
|
595
|
+
{/each}
|
|
596
|
+
</Select>
|
|
597
|
+
</div>
|
|
598
|
+
<div class="col">
|
|
599
|
+
<Input
|
|
600
|
+
type="text"
|
|
601
|
+
label={translate('ecommerce.configuration.conditionDescription', 'Condition Description')}
|
|
602
|
+
required={true}
|
|
603
|
+
bind:value={listing.ecommerceConditionDescription}
|
|
604
|
+
/>
|
|
605
|
+
</div>
|
|
606
|
+
</div>
|
|
607
|
+
<div class="form-row">
|
|
608
|
+
<div class="col">
|
|
609
|
+
<Textarea
|
|
610
|
+
label={translate('ecommerce.configuration.partDescription', 'Part Description')}
|
|
611
|
+
rows={4}
|
|
612
|
+
required={true}
|
|
613
|
+
class={{ 'is-invalid': !listing.inventoryDescription?.trim() }}
|
|
614
|
+
bind:value={listing.inventoryDescription}
|
|
615
|
+
/>
|
|
616
|
+
</div>
|
|
617
|
+
</div>
|
|
618
|
+
</div>
|
|
619
|
+
</div>
|
|
620
|
+
</div>
|
|
621
|
+
</div>
|
|
622
|
+
<div class="card hightlight-card mt-4 w-100">
|
|
623
|
+
<div class="card-header">{translate('ecommerce.configuration.shippingInfo', 'Shipping Info')}</div>
|
|
624
|
+
<div class="card-body">
|
|
625
|
+
<div class="form-row">
|
|
626
|
+
<div class="col-md">
|
|
627
|
+
<Input
|
|
628
|
+
inputmode="decimal"
|
|
629
|
+
type="number"
|
|
630
|
+
label={translate('ecommerce.configuration.weight', 'Weight')}
|
|
631
|
+
required={shippingFieldRequired}
|
|
632
|
+
bind:value={listing.weight}
|
|
633
|
+
></Input>
|
|
634
|
+
</div>
|
|
635
|
+
<div class="col-md">
|
|
636
|
+
<Select
|
|
637
|
+
label={translate('ecommerce.configuration.weightUnit', 'Weight Unit')}
|
|
638
|
+
emptyText="-- {translate('ecommerce.configuration.selectUnit', 'Select Unit')} --"
|
|
639
|
+
required={shippingFieldRequired}
|
|
640
|
+
bind:value={listing.weightUnit}
|
|
641
|
+
>
|
|
642
|
+
{#each weightUnits as unit}
|
|
643
|
+
<option value={unit}>{unit}</option>
|
|
644
|
+
{/each}
|
|
645
|
+
</Select>
|
|
646
|
+
</div>
|
|
647
|
+
</div>
|
|
648
|
+
<div class="form-row">
|
|
649
|
+
<div class="col-md">
|
|
650
|
+
<Input
|
|
651
|
+
inputmode="decimal"
|
|
652
|
+
type="number"
|
|
653
|
+
label={translate('ecommerce.configuration.length', 'Length')}
|
|
654
|
+
required={shippingFieldRequired}
|
|
655
|
+
bind:value={listing.shippingLength}
|
|
656
|
+
></Input>
|
|
657
|
+
</div>
|
|
658
|
+
<div class="col-md">
|
|
659
|
+
<Input
|
|
660
|
+
inputmode="decimal"
|
|
661
|
+
type="number"
|
|
662
|
+
label={translate('ecommerce.configuration.width', 'Width')}
|
|
663
|
+
required={shippingFieldRequired}
|
|
664
|
+
bind:value={listing.shippingWidth}
|
|
665
|
+
></Input>
|
|
666
|
+
</div>
|
|
667
|
+
</div>
|
|
668
|
+
<div class="form-row">
|
|
669
|
+
<div class="col-md">
|
|
670
|
+
<Input
|
|
671
|
+
inputmode="decimal"
|
|
672
|
+
type="number"
|
|
673
|
+
label={translate('ecommerce.configuration.height', 'Height')}
|
|
674
|
+
required={shippingFieldRequired}
|
|
675
|
+
bind:value={listing.shippingHeight}
|
|
676
|
+
></Input>
|
|
677
|
+
</div>
|
|
678
|
+
<div class="col-md">
|
|
679
|
+
<Select
|
|
680
|
+
label={translate('ecommerce.configuration.lengthUnit', 'Length Unit')}
|
|
681
|
+
emptyText="-- {translate('ecommerce.configuration.selectUnit', 'Select Unit')} --"
|
|
682
|
+
required={shippingFieldRequired}
|
|
683
|
+
bind:value={listing.shippingLengthUnit}
|
|
684
|
+
>
|
|
685
|
+
{#each lengthUnits as unit}
|
|
686
|
+
<option value={unit}>{unit}</option>
|
|
687
|
+
{/each}
|
|
688
|
+
</Select>
|
|
689
|
+
</div>
|
|
690
|
+
</div>
|
|
691
|
+
<div class="form-row">
|
|
692
|
+
<div class="col-md-6">
|
|
693
|
+
<Select
|
|
694
|
+
label={translate('ecommerce.configuration.packageType', 'Package Type')}
|
|
695
|
+
emptyText="-- {translate('ecommerce.configuration.selectPackage', 'Select Package')} --"
|
|
696
|
+
required={shippingFieldRequired}
|
|
697
|
+
bind:value={listing.shippingPackage}
|
|
698
|
+
>
|
|
699
|
+
{#each packageTypes as pkg}
|
|
700
|
+
<option value={pkg.partnerValue}>{pkg.name}</option>
|
|
701
|
+
{/each}
|
|
702
|
+
</Select>
|
|
703
|
+
</div>
|
|
704
|
+
</div>
|
|
705
|
+
</div>
|
|
706
|
+
</div>
|
|
707
|
+
</div>
|
|
708
|
+
<div
|
|
709
|
+
class="col-lg flex-wrap"
|
|
710
|
+
class:d-flex={showElement}
|
|
711
|
+
class:d-none={!showElement}
|
|
712
|
+
>
|
|
713
|
+
<div class="card hightlight-card mt-4 w-100">
|
|
714
|
+
<div class="card-header">{translate('ecommerce.configuration.listingDetails', 'Listing Details')}</div>
|
|
715
|
+
<div class="card-body">
|
|
716
|
+
<div class="form-row">
|
|
717
|
+
<div class="col">
|
|
718
|
+
<Input
|
|
719
|
+
type="text"
|
|
720
|
+
label={translate('ecommerce.configuration.title', 'Title')}
|
|
721
|
+
required={true}
|
|
722
|
+
bind:value={listing.listingTitle}
|
|
723
|
+
/>
|
|
724
|
+
</div>
|
|
725
|
+
</div>
|
|
726
|
+
<div class="form-row">
|
|
727
|
+
<div class="col">
|
|
728
|
+
<Textarea
|
|
729
|
+
label={translate('ecommerce.configuration.description', 'Description')}
|
|
730
|
+
rows={6}
|
|
731
|
+
required={true}
|
|
732
|
+
class={!listing.listingDescription || listing.listingDescription.trim() === '' ? 'is-invalid' : ''}
|
|
733
|
+
bind:value={listing.listingDescription}
|
|
734
|
+
/>
|
|
735
|
+
</div>
|
|
736
|
+
</div>
|
|
737
|
+
<div class="form-row">
|
|
738
|
+
<div class="col">
|
|
739
|
+
<Select
|
|
740
|
+
label={translate('ecommerce.configuration.ebayCategory', 'Ebay Category')}
|
|
741
|
+
emptyText="-- {translate('ecommerce.configuration.selectCategory', 'Select Category')} --"
|
|
742
|
+
required={true}
|
|
743
|
+
validation={{
|
|
744
|
+
validator: value => {
|
|
745
|
+
if (!value) {
|
|
746
|
+
false
|
|
747
|
+
}
|
|
748
|
+
return true
|
|
749
|
+
},
|
|
750
|
+
}}
|
|
751
|
+
bind:value={listing.ecommerceCategoryId}
|
|
752
|
+
>
|
|
753
|
+
{#each ebayCategoryList as category}
|
|
754
|
+
<option value={category.ebayCategoryId}>{category.name}</option>
|
|
755
|
+
{/each}
|
|
756
|
+
</Select>
|
|
757
|
+
</div>
|
|
758
|
+
</div>
|
|
759
|
+
<div class="form-row">
|
|
760
|
+
<div class="col">
|
|
761
|
+
<Input
|
|
762
|
+
type="number"
|
|
763
|
+
label={translate('ecommerce.configuration.fulfillmentTime', 'Fulfillment Time')}
|
|
764
|
+
onchange={handleSave}
|
|
765
|
+
required={true}
|
|
766
|
+
bind:value={listing.fulfillmentTime}
|
|
767
|
+
/>
|
|
768
|
+
</div>
|
|
769
|
+
<div class="col">
|
|
770
|
+
<Select
|
|
771
|
+
label={translate('ecommerce.configuration.fulfillmentTimeUnit', 'Fulfillment Time Unit')}
|
|
772
|
+
emptyText="-- {translate('ecommerce.configuration.selectUnit', 'Select Unit')} --"
|
|
773
|
+
required={true}
|
|
774
|
+
bind:value={listing.fulfillmentTimeUnit}
|
|
775
|
+
>
|
|
776
|
+
{#each ecommercePartnerStaticData.ebay.timeUnits as unit}
|
|
777
|
+
<option value={unit}>{unit}</option>
|
|
778
|
+
{/each}
|
|
779
|
+
</Select>
|
|
780
|
+
</div>
|
|
781
|
+
</div>
|
|
782
|
+
<PolicyList
|
|
783
|
+
title={translate('ecommerce.configuration.fulfillmentPolicies', 'Fulfillment Policies')}
|
|
784
|
+
policies={fulfillmentPolicyList}
|
|
785
|
+
selectedPolicyIds={Array.isArray(listing.partnerSpecificDetails.fulfillmentPolicy)
|
|
786
|
+
? listing.partnerSpecificDetails.fulfillmentPolicy
|
|
787
|
+
: listing.partnerSpecificDetails.fulfillmentPolicy
|
|
788
|
+
? [listing.partnerSpecificDetails.fulfillmentPolicy]
|
|
789
|
+
: []}
|
|
790
|
+
onSelectionChange={handleFulfillmentPoliciesChange}
|
|
791
|
+
/>
|
|
792
|
+
|
|
793
|
+
<PolicyList
|
|
794
|
+
title={translate('ecommerce.configuration.paymentPolicies', 'Payment Policies')}
|
|
795
|
+
policies={getPaymentPolicyListWithChecked()}
|
|
796
|
+
selectedPolicyIds={Array.isArray(listing.partnerSpecificDetails.paymentPolicy)
|
|
797
|
+
? listing.partnerSpecificDetails.paymentPolicy
|
|
798
|
+
: listing.partnerSpecificDetails.paymentPolicy
|
|
799
|
+
? [listing.partnerSpecificDetails.paymentPolicy]
|
|
800
|
+
: []}
|
|
801
|
+
onSelectionChange={handlePaymentPoliciesChange}
|
|
802
|
+
/>
|
|
803
|
+
|
|
804
|
+
<PolicyList
|
|
805
|
+
title={translate('ecommerce.configuration.returnPolicies', 'Return Policies')}
|
|
806
|
+
policies={getReturnPolicyListWithChecked()}
|
|
807
|
+
selectedPolicyIds={Array.isArray(listing.partnerSpecificDetails.returnPolicy)
|
|
808
|
+
? listing.partnerSpecificDetails.returnPolicy
|
|
809
|
+
: listing.partnerSpecificDetails.returnPolicy
|
|
810
|
+
? [listing.partnerSpecificDetails.returnPolicy]
|
|
811
|
+
: []}
|
|
812
|
+
onSelectionChange={handleReturnPoliciesChange}
|
|
813
|
+
/>
|
|
814
|
+
</div>
|
|
815
|
+
</div>
|
|
816
|
+
</div>
|
|
817
|
+
</div>
|
|
818
|
+
<div class="form-row">
|
|
819
|
+
<div class="col-lg d-flex flex-wrap mt-4">
|
|
820
|
+
<Button
|
|
821
|
+
outline
|
|
822
|
+
color="success"
|
|
823
|
+
size="sm"
|
|
824
|
+
iconClass="plus"
|
|
825
|
+
onclick={async () => {
|
|
826
|
+
if (
|
|
827
|
+
await userPrompt?.confirm(
|
|
828
|
+
translate(
|
|
829
|
+
'ecommerce.configuration.saveConfirm',
|
|
830
|
+
'Listing defaults will be overwritten and part or configuration changes will not update the listing',
|
|
831
|
+
),
|
|
832
|
+
)
|
|
833
|
+
) {
|
|
834
|
+
handleSave()
|
|
835
|
+
}
|
|
836
|
+
}}
|
|
837
|
+
>
|
|
838
|
+
{translate('ecommerce.configuration.manualSave', 'Manual Save')}
|
|
839
|
+
</Button>
|
|
840
|
+
</div>
|
|
841
|
+
</div>
|
|
842
|
+
|
|
843
|
+
<UserPrompt bind:this={userPrompt} />
|
|
844
|
+
<Modal
|
|
845
|
+
bind:show
|
|
846
|
+
title={translate('ecommerce.configuration.messageList', 'Message List')}
|
|
847
|
+
modalSize="lg"
|
|
848
|
+
confirmShown={false}
|
|
849
|
+
close={() => (show = false)}
|
|
850
|
+
>
|
|
851
|
+
{#each formatMessageForDisplay(listing.message) as msg}
|
|
852
|
+
<div>{msg}</div>
|
|
853
|
+
{/each}
|
|
854
|
+
</Modal>
|