@isoftdata/svelte-ecommerce 1.0.0-beta.3 → 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.
@@ -1,277 +1,283 @@
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 { booleanToYesNo } from '@isoftdata/utility-string'
13
- import Autocomplete from '@isoftdata/svelte-autocomplete'
14
- import EcommerceDefaults from './EcommerceDefaults.svelte'
15
- import type {
16
- EbayPolicy,
17
- EcommerceCondition,
18
- EcommercePartnerConfiguration,
19
- ExtendedInventoryTypeListingDefaults,
20
- InventoryType,
21
- InventoryTypeCategory,
22
- InventoryTypeListingDefaults,
23
- NewInventoryTypeListingDefaults,
24
- } from './utils.js'
25
-
26
- import { translate as defaultTranslate } from '@isoftdata/utility-string'
27
-
28
- const { t: translate } = getContext<i18n>('i18next') || { t: defaultTranslate }
29
-
30
- interface Props {
31
- // Props
32
- ebayPolicyList: Array<EbayPolicy>
33
- ecommerceConditionList: Array<EcommerceCondition>
34
- inventoryTypeList: Array<InventoryType>
35
- inventoryTypeWithDefaultConfigList: Array<ExtendedInventoryTypeListingDefaults>
36
- inventoryTypeCategoryList: Array<InventoryTypeCategory>
37
- partnerConfigurationList: Array<EcommercePartnerConfiguration>
38
- selectedEcommercePartnerId: number
39
- partTypeConfigurationChanged: (
40
- data: InventoryTypeListingDefaults | NewInventoryTypeListingDefaults,
41
- ) => Promise<void>
42
- }
43
-
44
- let {
45
- ebayPolicyList = [],
46
- ecommerceConditionList = [],
47
- inventoryTypeList = [],
48
- inventoryTypeWithDefaultConfigList = $bindable([]),
49
- inventoryTypeCategoryList = [],
50
- partnerConfigurationList = [],
51
- selectedEcommercePartnerId,
52
- partTypeConfigurationChanged = async (_: InventoryTypeListingDefaults | NewInventoryTypeListingDefaults) => {},
53
- }: Props = $props()
54
-
55
- // Template for new inventory type listing configurations
56
- const newInventoryTypeListingConfig: NewInventoryTypeListingDefaults = Object.freeze({
57
- active: true,
58
- categoryId: undefined,
59
- categoryName: '',
60
- defaults: {
61
- brandMapping: undefined,
62
- conditionDescription: undefined,
63
- conditionId: undefined,
64
- fulfillmentPolicies: undefined,
65
- fulfillmentTimeUnit: undefined,
66
- fulfillmentTimeValue: undefined,
67
- listingDescriptionTemplate: undefined,
68
- listingDuration: undefined,
69
- listingFormat: undefined,
70
- listingTitleTemplate: undefined,
71
- manufacturerPartNumberMapping: undefined,
72
- oemNumberMapping: undefined,
73
- packageType: undefined,
74
- paymentPolicies: undefined,
75
- pricingModifier: undefined,
76
- returnPolicies: undefined,
77
- shippingLengthUnit: undefined,
78
- shippingWeightUnit: undefined,
79
- storePickupAllowed: undefined,
80
- },
81
- ecommercePartnerId: 0,
82
- inventoryTypeId: 0,
83
- inventoryTypeListingConfigurationId: null,
84
- inventoryTypeName: '',
85
- inventoryType: null,
86
- descriptionTemplate: null,
87
- titleTemplate: null,
88
- })
89
-
90
- // Local state
91
- let isAddingListingConfig = false
92
- let selectedInventoryTypeListingConfig: InventoryTypeListingDefaults | NewInventoryTypeListingDefaults = $state(
93
- klona(newInventoryTypeListingConfig),
94
- )
95
- let show = $state(false)
96
- let editInventoryTypeDisabled: boolean = $state(false)
97
- let selectedInventoryType = $state<InventoryType | null>(null)
98
- // Hard coded temp data for drop downs
99
-
100
- // Automatically update filtered categories when inventoryType changes
101
- let filteredCategories = $derived.by(() => {
102
- // Use the inventoryType from the selectedInventoryTypeListingConfig if available
103
- const typeToUse = selectedInventoryTypeListingConfig?.inventoryTypeId
104
-
105
- // If no inventory type is selected, return empty array
106
- if (!typeToUse) {
107
- return []
108
- }
109
-
110
- // First find any categories with a typeSetId matching the selected inventory type
111
- const categoriesByInventoryType = inventoryTypeCategoryList.filter(category => category.typeSetId === typeToUse)
112
-
113
- // Now filter out the ones that aren't already configured by the inventorytype and category combo
114
- return categoriesByInventoryType.filter(category => {
115
- // Check if this category is already configured for this inventory type
116
- const isAlreadyConfigured = inventoryTypeWithDefaultConfigList.some(
117
- config => config.inventoryTypeId === typeToUse && config.categoryId === category.categoryId,
118
- )
119
-
120
- // Always include the currently selected category (for editing existing records)
121
- const isCurrentlySelected = selectedInventoryTypeListingConfig?.categoryId === category.categoryId
122
-
123
- // Return categories that aren't already configured OR are currently selected
124
- return !isAlreadyConfigured || isCurrentlySelected
125
- })
126
- })
127
-
128
- // Called on add or edit inventory type config
129
- function editInventoryTypeConfig(row?: InventoryTypeListingDefaults) {
130
- selectedInventoryTypeListingConfig = klona(row ?? newInventoryTypeListingConfig)
131
- editInventoryTypeDisabled = !!row
132
- // Sync the selectedInventoryType from the ID
133
- selectedInventoryType =
134
- inventoryTypeList.find(type => type.inventoryTypeId === selectedInventoryTypeListingConfig.inventoryTypeId) ??
135
- null
136
- show = true
137
- }
138
-
139
- // Function to prepare data and call for saving
140
- async function confirm() {
141
- if (!selectedInventoryTypeListingConfig) return
142
-
143
- // Create a copy of the data to avoid mutating state directly
144
- const listingConfigCopy = klona(selectedInventoryTypeListingConfig)
145
-
146
- // Call the parent save function with prepared data
147
- await partTypeConfigurationChanged(listingConfigCopy)
148
-
149
- // Close the modal after saving
150
- close()
151
- }
152
-
153
- function close() {
154
- // Reset to a completely fresh template to prevent any state contamination
155
- selectedInventoryTypeListingConfig = newInventoryTypeListingConfig
156
- selectedInventoryType = null
157
- editInventoryTypeDisabled = false
158
- show = false
159
- }
160
-
161
- function isRowSelected(row: ExtendedInventoryTypeListingDefaults): boolean {
162
- if (!selectedInventoryTypeListingConfig) return false
163
-
164
- // For existing records, compare IDs
165
- if ('inventoryTypeListingConfigurationId' in selectedInventoryTypeListingConfig) {
166
- return (
167
- selectedInventoryTypeListingConfig.inventoryTypeListingConfigurationId ===
168
- row.inventoryTypeListingConfigurationId
169
- )
170
- }
171
-
172
- // For new records, no row can be selected since they don't exist in the table yet
173
- return false
174
- }
175
- </script>
176
-
177
- <div class="card">
178
- <div class="card-header">
179
- <h4 class="mb-0">
180
- {translate('ecommerce.configuration.partTypeListingConfiguration', 'Part Type Listing Configuration')}
181
- </h4>
182
- <p class="text-muted mb-0">
183
- {translate(
184
- 'ecommerce.configuration.configurePartTypeCategory',
185
- 'Configure part type and category level settings for e-commerce listings',
186
- )}
187
- </p>
188
- </div>
189
- <div class="card-body">
190
- <Table
191
- rows={inventoryTypeWithDefaultConfigList}
192
- columns={[
193
- {
194
- property: 'inventoryTypeId',
195
- name: translate('ecommerce.configuration.typeNumber', 'Type #'),
196
- defaultSortColumn: true,
197
- },
198
- { property: 'inventoryTypeName', name: translate('ecommerce.configuration.name', 'Name') },
199
- { property: 'categoryName', name: translate('ecommerce.configuration.category', 'Category') },
200
- { property: 'active', name: translate('ecommerce.configuration.activeOnEbay', 'Active on eBay') },
201
- ]}
202
- >
203
- {#snippet children({ row })}
204
- <tr
205
- onclick={() => editInventoryTypeConfig(row)}
206
- style="cursor: pointer;"
207
- class:table-primary={isRowSelected(row)}
208
- >
209
- <td>{row.inventoryTypeId}</td>
210
- <td>{row.inventoryTypeName}</td>
211
- <td>{row.categoryName}</td>
212
- <td>{booleanToYesNo(row.active, translate)}</td>
213
- </tr>
214
- {/snippet}
215
- </Table>
216
- </div>
217
- <div class="card-footer">
218
- <Button
219
- outline
220
- color="success"
221
- size="sm"
222
- icon="plus"
223
- onclick={() => editInventoryTypeConfig()}
224
- disabled={isAddingListingConfig}
225
- >
226
- {translate('common:add', 'Add')}...
227
- </Button>
228
- </div>
229
- </div>
230
- <Modal
231
- bind:show
232
- title={translate('ecommerce.configuration.addEditPartTypeCategory', 'Add/Edit Part Type & Category Configuration')}
233
- size="lg"
234
- confirmButtonText={translate('ecommerce.configuration.save', 'Save')}
235
- {confirm}
236
- {close}
237
- >
238
- <Checkbox
239
- label={translate('ecommerce.configuration.activeOnEbay', 'Active On eBay')}
240
- bind:checked={selectedInventoryTypeListingConfig.active}
241
- />
242
- <div class="row">
243
- <div class="col-lg-4">
244
- <Autocomplete
245
- label={translate('ecommerce.configuration.partType', 'Part Type')}
246
- options={inventoryTypeList}
247
- bind:value={selectedInventoryType}
248
- getLabel={option => (option ? `${option.inventoryTypeId} - ${option.name}` : '')}
249
- change={option => {
250
- if (option) {
251
- selectedInventoryTypeListingConfig.inventoryTypeId = option.inventoryTypeId
252
- }
253
- }}
254
- />
255
- </div>
256
- <div class="col-lg-4">
257
- <Select
258
- label={translate('ecommerce.configuration.category', 'Category')}
259
- emptyText="-- {translate('ecommerce.configuration.selectCategory', 'Selectd Category')} --"
260
- disabled={!filteredCategories.length && !selectedInventoryTypeListingConfig.categoryId}
261
- bind:value={selectedInventoryTypeListingConfig.categoryId}
262
- >
263
- {#each filteredCategories as category}
264
- <option value={category.categoryId}>{category.name}</option>
265
- {/each}
266
- </Select>
267
- </div>
268
- </div>
269
- <hr />
270
- <EcommerceDefaults
271
- defaults={selectedInventoryTypeListingConfig.defaults}
272
- {ebayPolicyList}
273
- {ecommerceConditionList}
274
- {partnerConfigurationList}
275
- {selectedEcommercePartnerId}
276
- />
277
- </Modal>
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 { booleanToYesNo } from '@isoftdata/utility-string'
13
+ import Autocomplete from '@isoftdata/svelte-autocomplete'
14
+ import EcommerceDefaults from './EcommerceDefaults.svelte'
15
+ import type {
16
+ EbayPolicy,
17
+ EcommerceCondition,
18
+ EcommercePartnerConfiguration,
19
+ ExtendedInventoryTypeListingDefaults,
20
+ InventoryType,
21
+ InventoryTypeCategory,
22
+ InventoryTypeListingDefaults,
23
+ NewInventoryTypeListingDefaults,
24
+ } from './utils.js'
25
+
26
+ import { translate as defaultTranslate } from '@isoftdata/utility-string'
27
+
28
+ const { t: translate } = getContext<i18n>('i18next') || { t: defaultTranslate }
29
+
30
+ interface Props {
31
+ // Props
32
+ ebayPolicyList: Array<EbayPolicy>
33
+ ecommerceConditionList: Array<EcommerceCondition>
34
+ inventoryTypeList: Array<InventoryType>
35
+ inventoryTypeWithDefaultConfigList: Array<ExtendedInventoryTypeListingDefaults>
36
+ inventoryTypeCategoryList: Array<InventoryTypeCategory>
37
+ partnerConfigurationList: Array<EcommercePartnerConfiguration>
38
+ selectedEcommercePartnerId: number
39
+ partTypeConfigurationChanged: (
40
+ data: InventoryTypeListingDefaults | NewInventoryTypeListingDefaults,
41
+ ) => Promise<void>
42
+ }
43
+
44
+ let {
45
+ ebayPolicyList = [],
46
+ ecommerceConditionList = [],
47
+ inventoryTypeList = [],
48
+ inventoryTypeWithDefaultConfigList = $bindable([]),
49
+ inventoryTypeCategoryList = [],
50
+ partnerConfigurationList = [],
51
+ selectedEcommercePartnerId,
52
+ partTypeConfigurationChanged = async (_: InventoryTypeListingDefaults | NewInventoryTypeListingDefaults) => {},
53
+ }: Props = $props()
54
+
55
+ // Template for new inventory type listing configurations
56
+ const newInventoryTypeListingConfig: NewInventoryTypeListingDefaults = Object.freeze({
57
+ active: true,
58
+ categoryId: undefined,
59
+ categoryName: '',
60
+ defaults: {
61
+ brandMapping: undefined,
62
+ conditionDescription: undefined,
63
+ conditionId: undefined,
64
+ fulfillmentPolicies: undefined,
65
+ fulfillmentTimeUnit: undefined,
66
+ fulfillmentTimeValue: undefined,
67
+ listingDescriptionTemplate: undefined,
68
+ listingDuration: undefined,
69
+ listingFormat: undefined,
70
+ listingTitleTemplate: undefined,
71
+ manufacturerPartNumberMapping: undefined,
72
+ oemNumberMapping: undefined,
73
+ packageType: undefined,
74
+ paymentPolicies: undefined,
75
+ pricingModifier: undefined,
76
+ returnPolicies: undefined,
77
+ shippingLengthUnit: undefined,
78
+ shippingWeightUnit: undefined,
79
+ storePickupAllowed: undefined,
80
+ },
81
+ ecommercePartnerId: 0,
82
+ inventoryTypeId: 0,
83
+ inventoryTypeListingConfigurationId: null,
84
+ inventoryTypeName: '',
85
+ inventoryType: null,
86
+ descriptionTemplate: null,
87
+ titleTemplate: null,
88
+ })
89
+
90
+ // Local state
91
+ let isAddingListingConfig = false
92
+ let selectedInventoryTypeListingConfig: InventoryTypeListingDefaults | NewInventoryTypeListingDefaults = $state(
93
+ klona(newInventoryTypeListingConfig),
94
+ )
95
+ let show = $state(false)
96
+ let editInventoryTypeDisabled: boolean = $state(false)
97
+ let selectedInventoryType = $state<InventoryType | null>(null)
98
+ // Hard coded temp data for drop downs
99
+
100
+ // Automatically update filtered categories when inventoryType changes
101
+ let filteredCategories = $derived.by(() => {
102
+ // Use the inventoryType from the selectedInventoryTypeListingConfig if available
103
+ const typeToUse = selectedInventoryTypeListingConfig?.inventoryTypeId
104
+
105
+ // If no inventory type is selected, return empty array
106
+ if (!typeToUse) {
107
+ return []
108
+ }
109
+
110
+ // First find any categories with a typeSetId matching the selected inventory type
111
+ const categoriesByInventoryType = inventoryTypeCategoryList.filter(category => category.typeSetId === typeToUse)
112
+
113
+ // Now filter out the ones that aren't already configured by the inventorytype and category combo
114
+ return categoriesByInventoryType.filter(category => {
115
+ // Check if this category is already configured for this inventory type
116
+ const isAlreadyConfigured = inventoryTypeWithDefaultConfigList.some(
117
+ config => config.inventoryTypeId === typeToUse && config.categoryId === category.categoryId,
118
+ )
119
+
120
+ // Always include the currently selected category (for editing existing records)
121
+ const isCurrentlySelected = selectedInventoryTypeListingConfig?.categoryId === category.categoryId
122
+
123
+ // Return categories that aren't already configured OR are currently selected
124
+ return !isAlreadyConfigured || isCurrentlySelected
125
+ })
126
+ })
127
+
128
+ // Called on add or edit inventory type config
129
+ function editInventoryTypeConfig(row?: InventoryTypeListingDefaults) {
130
+ selectedInventoryTypeListingConfig = klona(row ?? newInventoryTypeListingConfig)
131
+
132
+ // Set ecommercePartnerId to the selected partner when creating a new config
133
+ if (!row) {
134
+ selectedInventoryTypeListingConfig.ecommercePartnerId = selectedEcommercePartnerId
135
+ }
136
+
137
+ editInventoryTypeDisabled = !!row
138
+ // Sync the selectedInventoryType from the ID
139
+ selectedInventoryType =
140
+ inventoryTypeList.find(type => type.inventoryTypeId === selectedInventoryTypeListingConfig.inventoryTypeId) ??
141
+ null
142
+ show = true
143
+ }
144
+
145
+ // Function to prepare data and call for saving
146
+ async function confirm() {
147
+ if (!selectedInventoryTypeListingConfig) return
148
+
149
+ // Create a copy of the data to avoid mutating state directly
150
+ const listingConfigCopy = klona(selectedInventoryTypeListingConfig)
151
+
152
+ // Call the parent save function with prepared data
153
+ await partTypeConfigurationChanged(listingConfigCopy)
154
+
155
+ // Close the modal after saving
156
+ close()
157
+ }
158
+
159
+ function close() {
160
+ // Reset to a completely fresh template to prevent any state contamination
161
+ selectedInventoryTypeListingConfig = newInventoryTypeListingConfig
162
+ selectedInventoryType = null
163
+ editInventoryTypeDisabled = false
164
+ show = false
165
+ }
166
+
167
+ function isRowSelected(row: ExtendedInventoryTypeListingDefaults): boolean {
168
+ if (!selectedInventoryTypeListingConfig) return false
169
+
170
+ // For existing records, compare IDs
171
+ if ('inventoryTypeListingConfigurationId' in selectedInventoryTypeListingConfig) {
172
+ return (
173
+ selectedInventoryTypeListingConfig.inventoryTypeListingConfigurationId ===
174
+ row.inventoryTypeListingConfigurationId
175
+ )
176
+ }
177
+
178
+ // For new records, no row can be selected since they don't exist in the table yet
179
+ return false
180
+ }
181
+ </script>
182
+
183
+ <div class="card">
184
+ <div class="card-header">
185
+ <h4 class="mb-0">
186
+ {translate('ecommerce.configuration.partTypeListingConfiguration', 'Part Type Listing Configuration')}
187
+ </h4>
188
+ <p class="text-muted mb-0">
189
+ {translate(
190
+ 'ecommerce.configuration.configurePartTypeCategory',
191
+ 'Configure part type and category level settings for e-commerce listings',
192
+ )}
193
+ </p>
194
+ </div>
195
+ <div class="card-body">
196
+ <Table
197
+ rows={inventoryTypeWithDefaultConfigList}
198
+ columns={[
199
+ {
200
+ property: 'inventoryTypeId',
201
+ name: translate('ecommerce.configuration.typeNumber', 'Type #'),
202
+ defaultSortColumn: true,
203
+ },
204
+ { property: 'inventoryTypeName', name: translate('ecommerce.configuration.name', 'Name') },
205
+ { property: 'categoryName', name: translate('ecommerce.configuration.category', 'Category') },
206
+ { property: 'active', name: translate('ecommerce.configuration.activeOnEbay', 'Active on eBay') },
207
+ ]}
208
+ >
209
+ {#snippet children({ row })}
210
+ <tr
211
+ onclick={() => editInventoryTypeConfig(row)}
212
+ style="cursor: pointer;"
213
+ class:table-primary={isRowSelected(row)}
214
+ >
215
+ <td>{row.inventoryTypeId}</td>
216
+ <td>{row.inventoryTypeName}</td>
217
+ <td>{row.categoryName}</td>
218
+ <td>{booleanToYesNo(row.active, translate)}</td>
219
+ </tr>
220
+ {/snippet}
221
+ </Table>
222
+ </div>
223
+ <div class="card-footer">
224
+ <Button
225
+ outline
226
+ color="success"
227
+ size="sm"
228
+ icon="plus"
229
+ onclick={() => editInventoryTypeConfig()}
230
+ disabled={isAddingListingConfig}
231
+ >
232
+ {translate('common:add', 'Add')}...
233
+ </Button>
234
+ </div>
235
+ </div>
236
+ <Modal
237
+ bind:show
238
+ title={translate('ecommerce.configuration.addEditPartTypeCategory', 'Add/Edit Part Type & Category Configuration')}
239
+ size="lg"
240
+ confirmButtonText={translate('ecommerce.configuration.save', 'Save')}
241
+ {confirm}
242
+ {close}
243
+ >
244
+ <Checkbox
245
+ label={translate('ecommerce.configuration.activeOnEbay', 'Active On eBay')}
246
+ bind:checked={selectedInventoryTypeListingConfig.active}
247
+ />
248
+ <div class="row">
249
+ <div class="col-lg-4">
250
+ <Autocomplete
251
+ label={translate('ecommerce.configuration.partType', 'Part Type')}
252
+ options={inventoryTypeList}
253
+ bind:value={selectedInventoryType}
254
+ getLabel={option => (option ? `${option.inventoryTypeId} - ${option.name}` : '')}
255
+ change={option => {
256
+ if (option) {
257
+ selectedInventoryTypeListingConfig.inventoryTypeId = option.inventoryTypeId
258
+ }
259
+ }}
260
+ />
261
+ </div>
262
+ <div class="col-lg-4">
263
+ <Select
264
+ label={translate('ecommerce.configuration.category', 'Category')}
265
+ emptyText="-- {translate('ecommerce.configuration.selectCategory', 'Selectd Category')} --"
266
+ disabled={!filteredCategories.length && !selectedInventoryTypeListingConfig.categoryId}
267
+ bind:value={selectedInventoryTypeListingConfig.categoryId}
268
+ >
269
+ {#each filteredCategories as category}
270
+ <option value={category.categoryId}>{category.name}</option>
271
+ {/each}
272
+ </Select>
273
+ </div>
274
+ </div>
275
+ <hr />
276
+ <EcommerceDefaults
277
+ bind:defaults={selectedInventoryTypeListingConfig.defaults}
278
+ {ebayPolicyList}
279
+ {ecommerceConditionList}
280
+ {partnerConfigurationList}
281
+ {selectedEcommercePartnerId}
282
+ />
283
+ </Modal>