@isoftdata/svelte-ecommerce 1.0.0-beta.0

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 (38) hide show
  1. package/README.md +58 -0
  2. package/dist/EcommerceCategoryMapConfiguration.svelte +266 -0
  3. package/dist/EcommerceCategoryMapConfiguration.svelte.d.ts +14 -0
  4. package/dist/EcommerceConditionMapConfiguration.svelte +214 -0
  5. package/dist/EcommerceConditionMapConfiguration.svelte.d.ts +10 -0
  6. package/dist/EcommerceConfiguration.svelte +195 -0
  7. package/dist/EcommerceConfiguration.svelte.d.ts +22 -0
  8. package/dist/EcommerceDefaults.svelte +357 -0
  9. package/dist/EcommerceDefaults.svelte.d.ts +12 -0
  10. package/dist/EcommerceListingDetails.svelte +986 -0
  11. package/dist/EcommerceListingDetails.svelte.d.ts +18 -0
  12. package/dist/EcommercePartTypeConfig.svelte +305 -0
  13. package/dist/EcommercePartTypeConfig.svelte.d.ts +15 -0
  14. package/dist/EcommerceStoreConfiguration.svelte +263 -0
  15. package/dist/EcommerceStoreConfiguration.svelte.d.ts +13 -0
  16. package/dist/PolicyList.svelte +66 -0
  17. package/dist/PolicyList.svelte.d.ts +10 -0
  18. package/dist/data/ebay.d.ts +11 -0
  19. package/dist/data/ebay.js +31 -0
  20. package/dist/data/htp.d.ts +9 -0
  21. package/dist/data/htp.js +22 -0
  22. package/dist/data/index.d.ts +4 -0
  23. package/dist/data/index.js +6 -0
  24. package/dist/data/types.d.ts +4 -0
  25. package/dist/data/types.js +1 -0
  26. package/dist/helpers/index.d.ts +3 -0
  27. package/dist/helpers/index.js +3 -0
  28. package/dist/helpers/listing.d.ts +22 -0
  29. package/dist/helpers/listing.js +324 -0
  30. package/dist/helpers/template.d.ts +31 -0
  31. package/dist/helpers/template.js +131 -0
  32. package/dist/helpers/validation.d.ts +2 -0
  33. package/dist/helpers/validation.js +91 -0
  34. package/dist/index.d.ts +11 -0
  35. package/dist/index.js +11 -0
  36. package/dist/utils.d.ts +321 -0
  37. package/dist/utils.js +1 -0
  38. package/package.json +66 -0
@@ -0,0 +1,18 @@
1
+ import type { EbayCategory, EbayPolicy, EcommerceCondition, ExtendedEcommerceConditionMap, EcommercePartnerConfiguration, ExtendedEbayCategoryMap, FileItem, InventoryListingDetail, InventoryRow, InventoryType, InventoryTypeListingDefaults, NewInventoryListingDetail } from './utils.js';
2
+ interface Props {
3
+ ebayCategoryList: EbayCategory[];
4
+ ebayCategoryMapList: ExtendedEbayCategoryMap[];
5
+ ebayPolicyList: EbayPolicy[];
6
+ ecommerceConditionMapList: ExtendedEcommerceConditionMap[];
7
+ ecommerceConditionList: EcommerceCondition[];
8
+ ecommercePartnerConfigurationList: EcommercePartnerConfiguration[];
9
+ listingList: InventoryListingDetail[];
10
+ partFileList: FileItem[];
11
+ part: InventoryRow;
12
+ inventoryTypeWithDefaultConfigList: InventoryTypeListingDefaults[];
13
+ inventoryTypeList: InventoryType[];
14
+ save: (data: InventoryListingDetail | NewInventoryListingDetail) => Promise<void>;
15
+ }
16
+ declare const EcommerceListingDetails: import("svelte").Component<Props, {}, "">;
17
+ type EcommerceListingDetails = ReturnType<typeof EcommerceListingDetails>;
18
+ export default EcommerceListingDetails;
@@ -0,0 +1,305 @@
1
+ <script lang="ts">
2
+ import type { i18n } from 'i18next'
3
+
4
+ import { getContext } from 'svelte'
5
+
6
+ import Table from '@isoftdata/svelte-table'
7
+ import Button from '@isoftdata/svelte-button'
8
+ import Select from '@isoftdata/svelte-select'
9
+ import Modal from '@isoftdata/svelte-modal'
10
+ import Checkbox from '@isoftdata/svelte-checkbox'
11
+ import klona from 'klona'
12
+ import type { InventoryType } from './utils.js'
13
+ import EcommerceDefaults from './EcommerceDefaults.svelte'
14
+ import type {
15
+ EbayPolicy,
16
+ EcommerceCondition,
17
+ EcommercePartnerConfiguration,
18
+ EcommerceSharedDefaults,
19
+ ExtendedInventoryTypeListingDefaults,
20
+ InventoryTypeCategory,
21
+ InventoryTypeListingDefaults,
22
+ NewInventoryTypeListingDefaults,
23
+ } from './utils.js'
24
+
25
+ import { translate as defaultTranslate } from '@isoftdata/utility-string'
26
+
27
+ const { t: translate } = getContext<i18n>('i18next') || { t: defaultTranslate }
28
+
29
+ interface Props {
30
+ // Props
31
+ ebayPolicyList: EbayPolicy[]
32
+ ecommerceConditionList: EcommerceCondition[]
33
+ inventoryTypeList: InventoryType[]
34
+ inventoryTypeWithDefaultConfigList: ExtendedInventoryTypeListingDefaults[]
35
+ inventoryTypeCategoryList: InventoryTypeCategory[]
36
+ partnerConfigurationList: EcommercePartnerConfiguration[]
37
+ selectedEcommercePartnerId: number
38
+ partTypeConfigurationChanged: (
39
+ data: InventoryTypeListingDefaults | NewInventoryTypeListingDefaults,
40
+ ) => Promise<void>
41
+ }
42
+
43
+ let {
44
+ ebayPolicyList = [],
45
+ ecommerceConditionList = [],
46
+ inventoryTypeList = [],
47
+ inventoryTypeWithDefaultConfigList = $bindable([]),
48
+ inventoryTypeCategoryList = [],
49
+ partnerConfigurationList = [],
50
+ selectedEcommercePartnerId,
51
+ partTypeConfigurationChanged = async (_: InventoryTypeListingDefaults | NewInventoryTypeListingDefaults) => {},
52
+ }: Props = $props()
53
+
54
+ // Template for new inventory type listing configurations
55
+ const newInventoryTypeListingConfig: NewInventoryTypeListingDefaults = Object.freeze({
56
+ active: true,
57
+ categoryId: undefined,
58
+ categoryName: '',
59
+ defaults: {
60
+ brandMapping: undefined,
61
+ conditionDescription: undefined,
62
+ conditionId: undefined,
63
+ fulfillmentPolicies: undefined,
64
+ fulfillmentTimeUnit: undefined,
65
+ fulfillmentTimeValue: undefined,
66
+ listingDescriptionTemplate: undefined,
67
+ listingDuration: undefined,
68
+ listingFormat: undefined,
69
+ listingTitleTemplate: undefined,
70
+ manufacturerPartNumberMapping: undefined,
71
+ oemNumberMapping: undefined,
72
+ packageType: undefined,
73
+ paymentPolicies: undefined,
74
+ pricingModifier: undefined,
75
+ returnPolicies: undefined,
76
+ shippingLengthUnit: undefined,
77
+ shippingWeightUnit: undefined,
78
+ storePickupAllowed: undefined,
79
+ },
80
+ ecommercePartnerId: 0,
81
+ inventoryTypeId: 0,
82
+ inventoryTypeListingConfigurationId: null,
83
+ inventoryTypeName: '',
84
+ inventoryType: null,
85
+ descriptionTemplate: null,
86
+ titleTemplate: null,
87
+ })
88
+
89
+ // Local state
90
+ let isAddingListingConfig = false
91
+ let selectedInventoryTypeListingConfig: InventoryTypeListingDefaults | NewInventoryTypeListingDefaults = $state(
92
+ klona(newInventoryTypeListingConfig),
93
+ )
94
+ let show = $state(false)
95
+ let editInventoryTypeDisabled: boolean = $state(false)
96
+ // Hard coded temp data for drop downs
97
+ let showAllOption = true
98
+
99
+ // Automatically update filtered categories when inventoryType changes
100
+ let filteredCategories = $derived.by(() => {
101
+ // Use the inventoryType from the selectedInventoryTypeListingConfig if available
102
+ const typeToUse = selectedInventoryTypeListingConfig?.inventoryTypeId
103
+
104
+ // If no inventory type is selected, return empty array
105
+ if (!typeToUse) {
106
+ return []
107
+ }
108
+
109
+ // First find any categories with a typeSetId matching the selected inventory type
110
+ const categoriesByInventoryType = inventoryTypeCategoryList.filter(category => category.typeSetId === typeToUse)
111
+
112
+ // Now filter out the ones that aren't already configured by the inventorytype and category combo
113
+ return categoriesByInventoryType.filter(category => {
114
+ // Check if this category is already configured for this inventory type
115
+ const isAlreadyConfigured = inventoryTypeWithDefaultConfigList.some(
116
+ config => config.inventoryTypeId === typeToUse && config.categoryId === category.categoryId,
117
+ )
118
+
119
+ // Always include the currently selected category (for editing existing records)
120
+ const isCurrentlySelected = selectedInventoryTypeListingConfig?.categoryId === category.categoryId
121
+
122
+ // Return categories that aren't already configured OR are currently selected
123
+ return !isAlreadyConfigured || isCurrentlySelected
124
+ })
125
+ })
126
+
127
+ // Called on add or edit inventory type config
128
+ function editInventoryTypeConfig(row?: InventoryTypeListingDefaults) {
129
+ if (row) {
130
+ // Editing existing row - clone the provided row
131
+ selectedInventoryTypeListingConfig = klona(row)
132
+ editInventoryTypeDisabled = true
133
+ } else {
134
+ // Adding new row - ensure completely fresh copy from helper function
135
+ selectedInventoryTypeListingConfig = newInventoryTypeListingConfig
136
+ editInventoryTypeDisabled = false
137
+ }
138
+
139
+ show = true
140
+ }
141
+
142
+ // Function to prepare data and call for saving
143
+ async function confirm() {
144
+ if (!selectedInventoryTypeListingConfig) return
145
+
146
+ // Create a copy of the data to avoid mutating state directly
147
+ const listingConfigCopy = klona(selectedInventoryTypeListingConfig)
148
+
149
+ // // TS hates this being possibly null
150
+ // if (!selectedInventoryTypeListingConfig.inventoryTypeId) {
151
+ // // Show error message or return early
152
+ // console.error('Inventory Type is required')
153
+ // return
154
+ // }
155
+
156
+ // Now create the object we want to save since there are some extras
157
+
158
+ // Call the parent save function with prepared data
159
+ await partTypeConfigurationChanged(listingConfigCopy)
160
+
161
+ // Close the modal after saving
162
+ close()
163
+ }
164
+
165
+ function close() {
166
+ // Reset to a completely fresh template to prevent any state contamination
167
+ selectedInventoryTypeListingConfig = newInventoryTypeListingConfig
168
+ editInventoryTypeDisabled = false
169
+ show = false
170
+ }
171
+
172
+ async function handlePartTypeDefaultsChange(newDefaults: EcommerceSharedDefaults) {
173
+ // This function is called when the EcommerceDefaults component changes
174
+ // The binding will automatically update selectedStoreConfigRow.defaults
175
+ // Don't need to do anything here since we're not auto-saving, feels dumb having an empty method
176
+ }
177
+
178
+ function isRowSelected(row: ExtendedInventoryTypeListingDefaults): boolean {
179
+ if (!selectedInventoryTypeListingConfig) return false
180
+
181
+ // For existing records, compare IDs
182
+ if ('inventoryTypeListingConfigurationId' in selectedInventoryTypeListingConfig) {
183
+ return (
184
+ selectedInventoryTypeListingConfig.inventoryTypeListingConfigurationId ===
185
+ row.inventoryTypeListingConfigurationId
186
+ )
187
+ }
188
+
189
+ // For new records, no row can be selected since they don't exist in the table yet
190
+ return false
191
+ }
192
+ </script>
193
+
194
+ <div class="card-header">
195
+ <h4 class="mb-0">
196
+ {translate('ecommerce.configuration.partTypeListingConfiguration', 'Part Type Listing Configuration')}
197
+ </h4>
198
+ <p class="text-muted mb-0">
199
+ {translate(
200
+ 'ecommerce.configuration.configurePartTypeCategory',
201
+ 'Configure part type and category level settings for e-commerce listings',
202
+ )}
203
+ </p>
204
+ </div>
205
+ <div class="card-body">
206
+ <div class="mb-3">
207
+ <Button
208
+ outline
209
+ color="success"
210
+ size="sm"
211
+ iconClass="plus"
212
+ onclick={() => editInventoryTypeConfig()}
213
+ disabled={isAddingListingConfig}
214
+ >
215
+ {translate('ecommerce.configuration.add', 'Add')}
216
+ </Button>
217
+ </div>
218
+ <Table
219
+ rows={inventoryTypeWithDefaultConfigList}
220
+ columns={[
221
+ {
222
+ property: 'inventoryTypeId',
223
+ name: translate('ecommerce.configuration.typeNumber', 'Type #'),
224
+ defaultSortColumn: true,
225
+ },
226
+ { property: 'inventoryTypeName', name: translate('ecommerce.configuration.name', 'Name') },
227
+ { property: 'categoryName', name: translate('ecommerce.configuration.category', 'Category') },
228
+ { property: 'active', name: translate('ecommerce.configuration.activeOnEbay', 'Active on eBay') },
229
+ ]}
230
+ >
231
+ {#snippet children({ row })}
232
+ <tr
233
+ onclick={() => editInventoryTypeConfig(row)}
234
+ class="cursor-pointer"
235
+ class:table-primary={isRowSelected(row)}
236
+ >
237
+ <td>{row.inventoryTypeId}</td>
238
+ <td>{row.inventoryTypeName}</td>
239
+ <td>{row.categoryName}</td>
240
+ <td
241
+ >{row.active
242
+ ? translate('ecommerce.configuration.yes', 'yes')
243
+ : translate('ecommerce.configuration.no', 'No')}</td
244
+ >
245
+ </tr>
246
+ {/snippet}
247
+ </Table>
248
+ </div>
249
+ <Modal
250
+ bind:show
251
+ title={translate('ecommerce.configuration.addEditPartTypeCategory', 'Add/Edit Part Type & Category Configuration')}
252
+ modalSize="lg"
253
+ confirmButtonText={translate('ecommerce.configuration.save', 'Save')}
254
+ cancelShown={true}
255
+ {confirm}
256
+ {close}
257
+ >
258
+ <Checkbox
259
+ label={translate('ecommerce.configuration.activeOnEbay', 'Active On eBay')}
260
+ bind:checked={selectedInventoryTypeListingConfig.active}
261
+ onchange={() => {
262
+ // Ensure active is always a boolean, not a string
263
+ selectedInventoryTypeListingConfig.active = !!selectedInventoryTypeListingConfig.active
264
+ }}
265
+ />
266
+ <div class="row">
267
+ <div class="col-md-3">
268
+ <Select
269
+ label={translate('ecommerce.configuration.partType', 'Part Type')}
270
+ showEmptyOption={showAllOption}
271
+ emptyValue={null}
272
+ emptyText={translate('ecommerce.configuration.selectType', 'Select Type')}
273
+ disabled={editInventoryTypeDisabled}
274
+ bind:value={selectedInventoryTypeListingConfig.inventoryTypeId}
275
+ >
276
+ {#each inventoryTypeList as type}
277
+ <option value={type.inventoryTypeId}>{type.inventoryTypeId} - {type.name}</option>
278
+ {/each}
279
+ </Select>
280
+ </div>
281
+ <div class="col-md-3">
282
+ <Select
283
+ label={translate('ecommerce.configuration.category', 'Category')}
284
+ showEmptyOption={showAllOption}
285
+ emptyValue={null}
286
+ emptyText={translate('ecommerce.configuration.selectCategory', 'Selectd Category')}
287
+ disabled={!filteredCategories.length && !selectedInventoryTypeListingConfig.categoryId}
288
+ bind:value={selectedInventoryTypeListingConfig.categoryId}
289
+ >
290
+ {#each filteredCategories as category}
291
+ <option value={category.categoryId}>{category.name}</option>
292
+ {/each}
293
+ </Select>
294
+ </div>
295
+ </div>
296
+ <hr />
297
+ <EcommerceDefaults
298
+ defaults={selectedInventoryTypeListingConfig.defaults}
299
+ defaultsChanged={handlePartTypeDefaultsChange}
300
+ {ebayPolicyList}
301
+ {ecommerceConditionList}
302
+ {partnerConfigurationList}
303
+ {selectedEcommercePartnerId}
304
+ />
305
+ </Modal>
@@ -0,0 +1,15 @@
1
+ import type { InventoryType } from './utils.js';
2
+ import type { EbayPolicy, EcommerceCondition, EcommercePartnerConfiguration, ExtendedInventoryTypeListingDefaults, InventoryTypeCategory, InventoryTypeListingDefaults, NewInventoryTypeListingDefaults } from './utils.js';
3
+ interface Props {
4
+ ebayPolicyList: EbayPolicy[];
5
+ ecommerceConditionList: EcommerceCondition[];
6
+ inventoryTypeList: InventoryType[];
7
+ inventoryTypeWithDefaultConfigList: ExtendedInventoryTypeListingDefaults[];
8
+ inventoryTypeCategoryList: InventoryTypeCategory[];
9
+ partnerConfigurationList: EcommercePartnerConfiguration[];
10
+ selectedEcommercePartnerId: number;
11
+ partTypeConfigurationChanged: (data: InventoryTypeListingDefaults | NewInventoryTypeListingDefaults) => Promise<void>;
12
+ }
13
+ declare const EcommercePartTypeConfig: import("svelte").Component<Props, {}, "inventoryTypeWithDefaultConfigList">;
14
+ type EcommercePartTypeConfig = ReturnType<typeof EcommercePartTypeConfig>;
15
+ export default EcommercePartTypeConfig;
@@ -0,0 +1,263 @@
1
+ <script lang="ts">
2
+ import type { i18n } from 'i18next'
3
+
4
+ import { getContext } from 'svelte'
5
+ import Table from '@isoftdata/svelte-table'
6
+ import klona from 'klona'
7
+ import Modal from '@isoftdata/svelte-modal'
8
+ import Checkbox from '@isoftdata/svelte-checkbox'
9
+ import Button from '@isoftdata/svelte-button'
10
+ import Select from '@isoftdata/svelte-select'
11
+ import type {
12
+ EbayLocation,
13
+ EbayPolicy,
14
+ EcommerceCondition,
15
+ EcommercePartnerConfiguration,
16
+ EcommerceStoreConfig,
17
+ Store,
18
+ EcommerceSharedDefaults,
19
+ } from './utils.js'
20
+ import EcommerceDefaults from './EcommerceDefaults.svelte'
21
+
22
+ import { translate as defaultTranslate } from '@isoftdata/utility-string'
23
+ const { t: translate } = getContext<i18n>('i18next') || { t: defaultTranslate }
24
+
25
+ interface Props {
26
+ ebayLocationList?: EbayLocation[]
27
+ ebayPolicyList?: EbayPolicy[]
28
+ ecommerceConditionList: EcommerceCondition[]
29
+ partnerConfigurationList?: EcommercePartnerConfiguration[]
30
+ selectedEcommercePartnerId: number
31
+ storeList: Store[]
32
+ partnerConfigurationChanged: (partnerConfig: EcommercePartnerConfiguration) => Promise<void>
33
+ }
34
+
35
+ let {
36
+ ebayLocationList = [],
37
+ ebayPolicyList = [],
38
+ ecommerceConditionList = [],
39
+ partnerConfigurationList = [],
40
+ selectedEcommercePartnerId,
41
+ storeList = [],
42
+ partnerConfigurationChanged = async (_: EcommercePartnerConfiguration) => {},
43
+ }: Props = $props()
44
+
45
+ let newStoreConfig: EcommerceStoreConfig = Object.freeze({
46
+ active: true,
47
+ defaults: {
48
+ brandMapping: undefined,
49
+ conditionDescription: undefined,
50
+ conditionId: undefined,
51
+ fulfillmentPolicies: undefined,
52
+ fulfillmentTimeUnit: undefined,
53
+ fulfillmentTimeValue: undefined,
54
+ listingDescriptionTemplate: undefined,
55
+ listingDuration: undefined,
56
+ listingFormat: undefined,
57
+ listingTitleTemplate: undefined,
58
+ manufacturerPartNumberMapping: undefined,
59
+ oemNumberMapping: undefined,
60
+ packageType: undefined,
61
+ paymentPolicies: undefined,
62
+ pricingModifier: undefined,
63
+ returnPolicies: undefined,
64
+ shippingLengthUnit: undefined,
65
+ shippingWeightUnit: undefined,
66
+ storePickupAllowed: undefined,
67
+ },
68
+ merchantLocationKey: undefined,
69
+ name: '',
70
+ storeId: 0,
71
+ })
72
+
73
+ let show = $state(false)
74
+ let showAllOption = $state(true)
75
+ let selectedStoreConfigRow = $state<EcommerceStoreConfig | null>(null)
76
+
77
+ // If we had more than one ecommercepartner, this would change depending on selected partner id
78
+ let selectedPartnerConfig = $derived(
79
+ partnerConfigurationList.find(row => row.ecommercePartnerId === selectedEcommercePartnerId),
80
+ )
81
+ // Get the store rows from the partnerconfig row
82
+ let selectedPartnerConfigStoreRows = $derived(selectedPartnerConfig?.defaults?.store || [])
83
+
84
+ // Filter available ebay locations depending on whether we're adding or editing
85
+ let availableLocations = $derived.by(() => {
86
+ const configuredEbayLocations = selectedPartnerConfigStoreRows.map(row => row.merchantLocationKey)
87
+ const isEditingExisting =
88
+ selectedStoreConfigRow?.name !== null &&
89
+ configuredEbayLocations.includes(selectedStoreConfigRow?.merchantLocationKey)
90
+
91
+ if (isEditingExisting) {
92
+ return ebayLocationList
93
+ }
94
+ return ebayLocationList.filter(location => !configuredEbayLocations.includes(location.ebayName))
95
+ })
96
+ // Filter available stores based on whether we're adding or editing existing
97
+ let availableStores = $derived.by(() => {
98
+ if (!selectedStoreConfigRow) return storeList
99
+
100
+ // Get the IDs of already configured stores
101
+ const configuredStoreIds = selectedPartnerConfigStoreRows.map(row => row.storeId)
102
+ const isEditingExisting =
103
+ selectedStoreConfigRow.storeId !== null && configuredStoreIds.includes(selectedStoreConfigRow.storeId)
104
+
105
+ if (isEditingExisting) {
106
+ return storeList
107
+ }
108
+ // If adding new, filter out already configured stores
109
+ return storeList.filter(store => !configuredStoreIds.includes(store.storeId))
110
+ })
111
+
112
+ async function confirm() {
113
+ if (!selectedStoreConfigRow || !selectedStoreConfigRow.storeId) return
114
+
115
+ // TODO: Here I am regretting my decision to have JSON sub object business for this
116
+ // Copy the store config data for uh state wackiness reasons?
117
+ const storeConfigRowCopy = klona(selectedStoreConfigRow)
118
+ // Copy the partner config row
119
+ const partnerConfigCopy = klona(selectedPartnerConfig)
120
+
121
+ // Copy the selectedParnterConfigStoreRows
122
+ let updatedStoreRows = klona(selectedPartnerConfigStoreRows || [])
123
+
124
+ // If empty, add storeConfigCopy
125
+ if (updatedStoreRows.length === 0) {
126
+ updatedStoreRows.push(storeConfigRowCopy)
127
+ } else {
128
+ const existingIndex = updatedStoreRows.findIndex(row => row.storeId === storeConfigRowCopy.storeId)
129
+ if (existingIndex === -1) {
130
+ updatedStoreRows.push(storeConfigRowCopy)
131
+ } else {
132
+ updatedStoreRows[existingIndex] = storeConfigRowCopy
133
+ }
134
+ }
135
+ // Still possible to be missing defaults?
136
+ if (partnerConfigCopy && partnerConfigCopy.defaults) {
137
+ partnerConfigCopy.defaults.store = updatedStoreRows
138
+ await partnerConfigurationChanged(partnerConfigCopy)
139
+ }
140
+ close()
141
+ }
142
+
143
+ function close() {
144
+ selectedStoreConfigRow = newStoreConfig
145
+ show = false
146
+ }
147
+
148
+ function editStoreConfigRow(row?: EcommerceStoreConfig) {
149
+ // Set selected row
150
+ selectedStoreConfigRow = klona(row || newStoreConfig)
151
+
152
+ show = true
153
+ }
154
+
155
+ async function handleStoreDefaultsChange(newDefaults: EcommerceSharedDefaults) {
156
+ // This function is called when the EcommerceDefaults component changes
157
+ // The binding will automatically update selectedStoreConfigRow.defaults
158
+ // Don't need to do anything here since we're not auto-saving, feels dumb having an empty method
159
+ }
160
+ </script>
161
+
162
+ <div class="card-header">
163
+ <h4 class="mb-0">{translate('ecommerce.configuration.storeConfiguration', 'Store Configuration')}</h4>
164
+ <p class="text-muted mb-0">
165
+ {translate('ecommerce.configuration.configureStoreLevelEbaySettings', 'Configure store level eBay settings')}
166
+ </p>
167
+ </div>
168
+ <div class="card-body">
169
+ <div class="mb-3">
170
+ <Button
171
+ outline
172
+ color="success"
173
+ size="sm"
174
+ iconClass="plus"
175
+ onclick={() => editStoreConfigRow()}
176
+ disabled={false}
177
+ >
178
+ {translate('ecommerce.configuration.add', 'Add')}
179
+ </Button>
180
+ </div>
181
+ {#if selectedPartnerConfigStoreRows.length}
182
+ <Table
183
+ rows={selectedPartnerConfigStoreRows}
184
+ responsive
185
+ stickyHeader
186
+ parentClass="overflow-y-auto mh-400"
187
+ columns={[
188
+ { property: 'storeId', name: translate('ecommerce.configuration.storeId', 'Store ID') },
189
+ { property: 'name', name: translate('ecommerce.configuration.storeName', 'Store Name') },
190
+ { property: 'merchantLocationKey', name: translate('ecommerce.configuration.ebayLocation', 'Ebay Location') },
191
+ { property: 'active', name: translate('ecommerce.configuration.active', 'Active') },
192
+ ]}
193
+ >
194
+ {#snippet children({ row })}
195
+ <tr
196
+ onclick={() => editStoreConfigRow(row as EcommerceStoreConfig)}
197
+ class="cursor-pointer"
198
+ >
199
+ <td property="storeId">{row.storeId}</td>
200
+ <td property="name">{row.name}</td>
201
+ <td property="merchantLocationKey">{row.merchantLocationKey}</td>
202
+ <td property="active">{row.active}</td>
203
+ </tr>
204
+ {/snippet}
205
+ </Table>
206
+ {/if}
207
+ </div>
208
+
209
+ <Modal
210
+ bind:show
211
+ title={translate('ecommerce.configuration.addEditStoreDefaults', 'Add/Edit Store Defaults')}
212
+ modalSize="lg"
213
+ confirmButtonText={translate('ecommerce.configuration.save', 'Save')}
214
+ cancelShown={true}
215
+ {confirm}
216
+ {close}
217
+ >
218
+ {#if selectedStoreConfigRow}
219
+ <div class="card-body">
220
+ <Checkbox
221
+ label={translate('ecommerce.configuration.activeOnEbay', 'Active on eBay')}
222
+ bind:checked={selectedStoreConfigRow.active}
223
+ />
224
+
225
+ <Select
226
+ label={translate('ecommerce.configuration.store', 'Store')}
227
+ showEmptyOption={showAllOption}
228
+ emptyValue={null}
229
+ emptyText={translate('ecommerce.configuration.selectStore', 'Select Store')}
230
+ disabled={false}
231
+ bind:value={selectedStoreConfigRow.storeId}
232
+ >
233
+ {#each availableStores as store}
234
+ <option value={store.storeId}>{store.name}</option>
235
+ {/each}
236
+ </Select>
237
+ {#if selectedStoreConfigRow.storeId}
238
+ <Select
239
+ label={translate('ecommerce.configuration.ebayLocation', 'Ebay Location')}
240
+ showEmptyOption={showAllOption}
241
+ emptyValue={null}
242
+ emptyText={translate('ecommerce.configuration.selectLocation', 'Select Location')}
243
+ disabled={false}
244
+ bind:value={selectedStoreConfigRow.merchantLocationKey}
245
+ >
246
+ {#each availableLocations as location}
247
+ <option value={location.ebayName}>{location.ebayName}</option>
248
+ {/each}
249
+ </Select>
250
+ {/if}
251
+ {#if selectedStoreConfigRow.merchantLocationKey}
252
+ <EcommerceDefaults
253
+ bind:defaults={selectedStoreConfigRow.defaults}
254
+ {ebayPolicyList}
255
+ {ecommerceConditionList}
256
+ {partnerConfigurationList}
257
+ defaultsChanged={handleStoreDefaultsChange}
258
+ {selectedEcommercePartnerId}
259
+ />
260
+ {/if}
261
+ </div>
262
+ {/if}
263
+ </Modal>
@@ -0,0 +1,13 @@
1
+ import type { EbayLocation, EbayPolicy, EcommerceCondition, EcommercePartnerConfiguration, Store } from './utils.js';
2
+ interface Props {
3
+ ebayLocationList?: EbayLocation[];
4
+ ebayPolicyList?: EbayPolicy[];
5
+ ecommerceConditionList: EcommerceCondition[];
6
+ partnerConfigurationList?: EcommercePartnerConfiguration[];
7
+ selectedEcommercePartnerId: number;
8
+ storeList: Store[];
9
+ partnerConfigurationChanged: (partnerConfig: EcommercePartnerConfiguration) => Promise<void>;
10
+ }
11
+ declare const EcommerceStoreConfiguration: import("svelte").Component<Props, {}, "">;
12
+ type EcommerceStoreConfiguration = ReturnType<typeof EcommerceStoreConfiguration>;
13
+ export default EcommerceStoreConfiguration;
@@ -0,0 +1,66 @@
1
+ <script lang="ts">
2
+ import type { i18n } from 'i18next'
3
+
4
+ import { getContext } from 'svelte'
5
+
6
+ import Table, { Td } from '@isoftdata/svelte-table'
7
+ import Checkbox from '@isoftdata/svelte-checkbox'
8
+ import { translate as defaultTranslate } from '@isoftdata/utility-string'
9
+
10
+ const { t: translate } = getContext<i18n>('i18next') || { t: defaultTranslate }
11
+
12
+ import type { PolicyRowWithChecked } from './utils.js'
13
+
14
+ interface Props {
15
+ title: string
16
+ policies: PolicyRowWithChecked[]
17
+ selectedPolicyIds: string[]
18
+ onSelectionChange: (selectedIds: string[]) => void
19
+ }
20
+
21
+ let { title, policies, selectedPolicyIds, onSelectionChange }: Props = $props()
22
+
23
+ function handleCheckboxChange(policy: PolicyRowWithChecked, checked: boolean) {
24
+ let updatedIds: string[]
25
+
26
+ if (checked) {
27
+ // Add policy to the list if not already present
28
+ if (!selectedPolicyIds.includes(policy.policyId)) {
29
+ updatedIds = [...selectedPolicyIds, policy.policyId]
30
+ } else {
31
+ updatedIds = selectedPolicyIds
32
+ }
33
+ } else {
34
+ // Remove policy from the list
35
+ updatedIds = selectedPolicyIds.filter(id => id !== policy.policyId)
36
+ }
37
+
38
+ onSelectionChange(updatedIds)
39
+ }
40
+ </script>
41
+
42
+ <div class="mb-4">
43
+ <Table
44
+ responsive
45
+ {title}
46
+ stickyHeader
47
+ rows={policies}
48
+ parentClass="overflow-y-auto mh-500"
49
+ columns={[
50
+ { property: 'checked', name: translate('configuration.ecommerce.selected', 'Selected'), width: '100px' },
51
+ { property: 'name', name: translate('configuration.ecommerce.policy', 'Policy'), defaultSortColumn: true },
52
+ ]}
53
+ >
54
+ {#snippet children({ row })}
55
+ <tr>
56
+ <Td property="checked">
57
+ <Checkbox
58
+ checked={row.checked}
59
+ onchange={e => handleCheckboxChange(row, e.currentTarget.checked)}
60
+ />
61
+ </Td>
62
+ <td>{row.name}</td>
63
+ </tr>
64
+ {/snippet}
65
+ </Table>
66
+ </div>
@@ -0,0 +1,10 @@
1
+ import type { PolicyRowWithChecked } from './utils.js';
2
+ interface Props {
3
+ title: string;
4
+ policies: PolicyRowWithChecked[];
5
+ selectedPolicyIds: string[];
6
+ onSelectionChange: (selectedIds: string[]) => void;
7
+ }
8
+ declare const PolicyList: import("svelte").Component<Props, {}, "">;
9
+ type PolicyList = ReturnType<typeof PolicyList>;
10
+ export default PolicyList;