@stacksjs/defaults 0.75.279 → 0.75.280
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/ide/vscode/package.json +1 -1
- package/package.json +2 -2
- package/resources/components/Dashboard/Analytics/CampaignAnalytics.stx +28 -30
- package/resources/components/Dashboard/Commerce/CommerceCategoriesDashboard.stx +8 -28
- package/resources/components/Dashboard/Commerce/CommerceCategoriesTable.stx +72 -29
- package/resources/components/Dashboard/Commerce/CommerceCouponsDashboard.stx +8 -29
- package/resources/components/Dashboard/Commerce/CommerceCouponsTable.stx +69 -49
- package/resources/components/Dashboard/Commerce/CommerceCustomersDashboard.stx +8 -27
- package/resources/components/Dashboard/Commerce/CommerceCustomersTable.stx +70 -48
- package/resources/components/Dashboard/Commerce/CommerceGiftCardsDashboard.stx +8 -29
- package/resources/components/Dashboard/Commerce/CommerceGiftCardsTable.stx +72 -82
- package/resources/components/Dashboard/Commerce/CommerceManufacturersDashboard.stx +1 -1
- package/resources/components/Dashboard/Commerce/CommerceManufacturersTable.stx +3 -0
- package/resources/components/Dashboard/Commerce/CommerceOrdersDashboard.stx +8 -28
- package/resources/components/Dashboard/Commerce/CommerceOrdersTable.stx +76 -49
- package/resources/components/Dashboard/Commerce/CommercePaymentsDashboard.stx +1 -1
- package/resources/components/Dashboard/Commerce/CommercePaymentsTable.stx +3 -0
- package/resources/components/Dashboard/Commerce/CommerceProductsDashboard.stx +8 -28
- package/resources/components/Dashboard/Commerce/CommerceProductsTable.stx +81 -34
- package/resources/components/Dashboard/Commerce/CommerceReviewsDashboard.stx +11 -37
- package/resources/components/Dashboard/Commerce/CommerceReviewsTable.stx +106 -77
- package/resources/components/Dashboard/Commerce/CommerceTaxesDashboard.stx +1 -1
- package/resources/components/Dashboard/Commerce/CommerceTaxesTable.stx +3 -0
- package/resources/components/Dashboard/Commerce/CommerceUnitsDashboard.stx +1 -1
- package/resources/components/Dashboard/Commerce/CommerceUnitsTable.stx +3 -0
- package/resources/components/Dashboard/Commerce/CommerceVariantsDashboard.stx +1 -1
- package/resources/components/Dashboard/Commerce/CommerceVariantsTable.stx +3 -0
- package/resources/components/Dashboard/Commerce/Delivery/CouriersDashboard.stx +1 -11
- package/resources/components/Dashboard/Commerce/Delivery/CouriersTable.stx +55 -82
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryRoutesDashboard.stx +1 -12
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryRoutesTable.stx +57 -86
- package/resources/components/Dashboard/Commerce/Delivery/DigitalDeliveriesDashboard.stx +1 -24
- package/resources/components/Dashboard/Commerce/Delivery/DigitalDeliveryTable.stx +62 -80
- package/resources/components/Dashboard/Commerce/Delivery/LicenseKeysDashboard.stx +1 -12
- package/resources/components/Dashboard/Commerce/Delivery/LicenseKeysTable.stx +58 -99
- package/resources/components/Dashboard/Commerce/Delivery/ShippingMethodsDashboard.stx +1 -23
- package/resources/components/Dashboard/Commerce/Delivery/ShippingMethodsTable.stx +51 -86
- package/resources/components/Dashboard/Commerce/Delivery/ShippingRatesDashboard.stx +1 -12
- package/resources/components/Dashboard/Commerce/Delivery/ShippingRatesTable.stx +58 -71
- package/resources/components/Dashboard/Commerce/Delivery/ShippingZonesDashboard.stx +1 -12
- package/resources/components/Dashboard/Commerce/Delivery/ShippingZonesTable.stx +85 -110
- package/resources/components/Dashboard/Commerce/PrintDevicesDashboard.stx +8 -28
- package/resources/components/Dashboard/Commerce/PrintDevicesTable.stx +62 -57
- package/resources/components/Dashboard/Marketing/AbandonedCartsDashboard.stx +7 -24
- package/resources/components/Dashboard/Marketing/AbandonedCartsTable.stx +63 -59
- package/resources/components/Dashboard/Marketing/CampaignsDashboard.stx +7 -25
- package/resources/components/Dashboard/Marketing/CampaignsTable.stx +80 -57
- package/resources/components/Dashboard/Marketing/MarketingListsDashboard.stx +7 -27
- package/resources/components/Dashboard/Marketing/MarketingListsTable.stx +66 -71
- package/resources/components/Dashboard/Marketing/SocialPostsDashboard.stx +7 -25
- package/resources/components/Dashboard/Marketing/SocialPostsTable.stx +75 -62
- package/resources/components/Dashboard/Monitoring/ErrorDashboard.stx +9 -50
- package/resources/components/Dashboard/Monitoring/ErrorTable.stx +68 -97
- package/resources/components/Dashboard/Notifications/NotificationDeliveries.stx +22 -44
- package/resources/components/Dashboard/Notifications/NotificationDeliveryHistory.stx +13 -4
- package/resources/components/Dashboard/Notifications/NotificationDeliveryTable.stx +74 -86
- package/resources/components/Dashboard/UI/Table.stx +41 -2
|
@@ -29,8 +29,6 @@ const records = state<ShippingMethodRecord[]>([])
|
|
|
29
29
|
const loading = state(true)
|
|
30
30
|
const loadError = state('')
|
|
31
31
|
const search = state('')
|
|
32
|
-
const page = state(1)
|
|
33
|
-
const perPage = state(10)
|
|
34
32
|
const editorOpen = state(false)
|
|
35
33
|
const editorMode = state<'create' | 'edit'>('create')
|
|
36
34
|
const editing = state<ShippingMethodRecord | null>(null)
|
|
@@ -51,11 +49,6 @@ function filteredRecords(): ShippingMethodRecord[] {
|
|
|
51
49
|
].some(value => String(value || '').toLowerCase().includes(query)))
|
|
52
50
|
}
|
|
53
51
|
|
|
54
|
-
function paginatedRecords(): ShippingMethodRecord[] {
|
|
55
|
-
const start = (page() - 1) * perPage()
|
|
56
|
-
return filteredRecords().slice(start, start + perPage())
|
|
57
|
-
}
|
|
58
|
-
|
|
59
52
|
function activeCount(): number {
|
|
60
53
|
return records().filter(record => record.status === 'active').length
|
|
61
54
|
}
|
|
@@ -80,9 +73,6 @@ async function loadMethods(): Promise<void> {
|
|
|
80
73
|
if (!Array.isArray(data))
|
|
81
74
|
throw new TypeError('Shipping methods response was not an array.')
|
|
82
75
|
records.set(data)
|
|
83
|
-
const lastPage = Math.max(1, Math.ceil(filteredRecords().length / perPage()))
|
|
84
|
-
if (page() > lastPage)
|
|
85
|
-
page.set(lastPage)
|
|
86
76
|
}
|
|
87
77
|
catch (error) {
|
|
88
78
|
loadError.set(error instanceof Error ? error.message : String(error))
|
|
@@ -95,7 +85,6 @@ async function loadMethods(): Promise<void> {
|
|
|
95
85
|
|
|
96
86
|
function handleSearch(value: string): void {
|
|
97
87
|
search.set(value)
|
|
98
|
-
page.set(1)
|
|
99
88
|
}
|
|
100
89
|
|
|
101
90
|
function openCreate(): void {
|
|
@@ -184,9 +173,6 @@ async function confirmDelete(): Promise<void> {
|
|
|
184
173
|
})
|
|
185
174
|
records.set(records().filter(record => record.id !== current.id))
|
|
186
175
|
deleting.set(null)
|
|
187
|
-
const lastPage = Math.max(1, Math.ceil(filteredRecords().length / perPage()))
|
|
188
|
-
if (page() > lastPage)
|
|
189
|
-
page.set(lastPage)
|
|
190
176
|
pushToast('success', 'Shipping method deleted', { detail: current.name })
|
|
191
177
|
}
|
|
192
178
|
catch (error) {
|
|
@@ -197,10 +183,6 @@ async function confirmDelete(): Promise<void> {
|
|
|
197
183
|
}
|
|
198
184
|
}
|
|
199
185
|
|
|
200
|
-
function goToPage(value: number): void {
|
|
201
|
-
page.set(value)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
186
|
onMount(() => {
|
|
205
187
|
void loadMethods()
|
|
206
188
|
})
|
|
@@ -225,11 +207,7 @@ onMount(() => {
|
|
|
225
207
|
<div><h2 class="font-semibold text-gray-900 dark:text-white">Configured methods</h2><p class="mt-0.5 text-gray-500 text-xs dark:text-neutral-400">{{ filteredRecords().length }} matching records</p></div>
|
|
226
208
|
<SearchFilter placeholder="Search shipping methods" class="w-full sm:w-80" @search="handleSearch" />
|
|
227
209
|
</div>
|
|
228
|
-
<
|
|
229
|
-
<ShippingMethodsTable :if="!loading()" :methods="paginatedRecords()" @edit="openEdit" @delete="requestDelete" />
|
|
230
|
-
<div :if="!loading()" class="px-4 py-3 bg-gray-50 dark:bg-neutral-900/50 border-gray-200 border-t dark:border-neutral-700">
|
|
231
|
-
<DeliveryPagination :current-page="page()" :total-items="filteredRecords().length" :items-per-page="perPage()" @select="goToPage" />
|
|
232
|
-
</div>
|
|
210
|
+
<ShippingMethodsTable :methods="filteredRecords()" :loading="loading()" :page-reset-key="search()" @edit="openEdit" @delete="requestDelete" />
|
|
233
211
|
</section>
|
|
234
212
|
</div>
|
|
235
213
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
@import('../../UI/Table')
|
|
2
|
+
|
|
1
3
|
<script client>
|
|
2
|
-
import { formatCurrency as formatMinorUnits } from '@stacksjs/commerce/money'
|
|
3
4
|
interface ShippingZoneSummary {
|
|
4
5
|
id: number
|
|
5
6
|
name: string
|
|
@@ -16,96 +17,60 @@ interface ShippingMethodRow {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
const methods = useReactiveProp('methods', [] as ShippingMethodRow[])
|
|
20
|
+
const loading = useReactiveProp('loading', false)
|
|
21
|
+
/** The parent's search: a change returns to the first page. */
|
|
22
|
+
const pageResetKey = useReactiveProp<unknown>('pageResetKey', '')
|
|
19
23
|
const emit = defineEmits()
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
/** Shipping amounts are stored as integer cents, in USD: currency columns fixed to it. */
|
|
26
|
+
const COLUMNS = [
|
|
27
|
+
{ key: 'name', label: 'Name', sortable: true, class: 'font-medium text-stone-800 whitespace-nowrap dark:text-white' },
|
|
28
|
+
{ key: 'description', label: 'Description', emptyText: 'No description', class: 'max-w-xs truncate' },
|
|
29
|
+
{ key: 'base_rate', label: 'Base rate', type: 'currency' as const, currency: 'USD', sortable: true, emptyText: 'Not available', class: 'tabular-nums whitespace-nowrap' },
|
|
30
|
+
{ key: 'free_shipping', label: 'Free shipping', type: 'currency' as const, currency: 'USD', sortable: true, emptyText: 'No minimum', class: 'tabular-nums whitespace-nowrap' },
|
|
31
|
+
{ key: 'zoneCount', label: 'Zones', type: 'number' as const, sortable: true, class: 'tabular-nums' },
|
|
32
|
+
{
|
|
33
|
+
key: 'status',
|
|
34
|
+
label: 'Status',
|
|
35
|
+
type: 'badge' as const,
|
|
36
|
+
sortable: true,
|
|
37
|
+
badgeClasses: {
|
|
38
|
+
active: 'bg-emerald-50 text-emerald-700 dark:bg-emerald-400/10 dark:text-emerald-300',
|
|
39
|
+
draft: 'bg-amber-50 text-amber-700 dark:bg-amber-400/10 dark:text-amber-300',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
]
|
|
25
43
|
|
|
26
|
-
|
|
27
|
-
}
|
|
44
|
+
const ROW_ACTIONS = [
|
|
45
|
+
{ action: 'edit', label: 'Edit', icon: 'i-hugeicons-edit-01' },
|
|
46
|
+
{ action: 'delete', label: 'Delete', icon: 'i-hugeicons-waste', class: 'text-red-500' },
|
|
47
|
+
]
|
|
28
48
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return 'bg-amber-50 text-amber-700 ring-amber-600/20 dark:bg-amber-400/10 dark:text-amber-300 dark:ring-amber-400/20'
|
|
49
|
+
const rows = derived(() => methods().map(method => ({
|
|
50
|
+
...method,
|
|
51
|
+
zoneCount: method.shipping_zones?.length || 0,
|
|
52
|
+
})))
|
|
34
53
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
function formatStatus(status: string): string {
|
|
39
|
-
return status.charAt(0).toUpperCase() + status.slice(1)
|
|
54
|
+
function handleRowAction(payload: { action: string, row: ShippingMethodRow }): void {
|
|
55
|
+
emit(payload.action, payload.row)
|
|
40
56
|
}
|
|
41
57
|
</script>
|
|
42
58
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
<td class="pl-6 pr-3 py-3.5 font-medium text-sm text-stone-800 whitespace-nowrap dark:text-white">
|
|
62
|
-
{{ method.name }}
|
|
63
|
-
</td>
|
|
64
|
-
<td class="px-4 py-3.5 max-w-xs text-sm text-stone-500 truncate dark:text-neutral-300">
|
|
65
|
-
{{ method.description || 'No description' }}
|
|
66
|
-
</td>
|
|
67
|
-
<td class="px-4 py-3.5 tabular-nums text-sm text-stone-500 whitespace-nowrap dark:text-neutral-300">
|
|
68
|
-
{{ formatCurrency(method.base_rate) }}
|
|
69
|
-
</td>
|
|
70
|
-
<td class="px-4 py-3.5 tabular-nums text-sm text-stone-500 whitespace-nowrap dark:text-neutral-300">
|
|
71
|
-
{{ method.free_shipping === null || method.free_shipping === undefined ? 'No minimum' : formatCurrency(method.free_shipping) }}
|
|
72
|
-
</td>
|
|
73
|
-
<td class="px-4 py-3.5 tabular-nums text-sm text-stone-500 whitespace-nowrap dark:text-neutral-300">
|
|
74
|
-
{{ method.shipping_zones?.length || 0 }}
|
|
75
|
-
</td>
|
|
76
|
-
<td class="px-4 py-3.5 text-sm whitespace-nowrap">
|
|
77
|
-
<span :class="'inline-flex px-2.5 py-1 font-medium text-xs ring-1 ring-inset rounded-full ' + statusClass(method.status)">
|
|
78
|
-
{{ formatStatus(method.status) }}
|
|
79
|
-
</span>
|
|
80
|
-
</td>
|
|
81
|
-
<td class="relative pl-3 pr-6 py-3.5 font-medium text-right text-sm whitespace-nowrap">
|
|
82
|
-
<Button
|
|
83
|
-
variant="ghost"
|
|
84
|
-
size="sm"
|
|
85
|
-
iconOnly
|
|
86
|
-
@click="emit('edit', method)"
|
|
87
|
-
:ariaLabel="'Edit ' + method.name"
|
|
88
|
-
>
|
|
89
|
-
<span class="block h-4 w-4 text-blue-500 i-hugeicons-edit-01"></span>
|
|
90
|
-
</Button>
|
|
91
|
-
<Button
|
|
92
|
-
variant="ghost"
|
|
93
|
-
size="sm"
|
|
94
|
-
iconOnly
|
|
95
|
-
@click="emit('delete', method)"
|
|
96
|
-
:ariaLabel="'Delete ' + method.name"
|
|
97
|
-
>
|
|
98
|
-
<span class="block h-4 w-4 text-red-500 i-hugeicons-waste"></span>
|
|
99
|
-
</Button>
|
|
100
|
-
</td>
|
|
101
|
-
</tr>
|
|
102
|
-
</template>
|
|
103
|
-
|
|
104
|
-
<tr :if="methods().length === 0">
|
|
105
|
-
<td colspan="7" class="px-6 py-12 text-center text-sm text-stone-500 dark:text-neutral-400">
|
|
106
|
-
No shipping methods found.
|
|
107
|
-
</td>
|
|
108
|
-
</tr>
|
|
109
|
-
</tbody>
|
|
110
|
-
</table>
|
|
111
|
-
</div>
|
|
59
|
+
{{--
|
|
60
|
+
No `searchable` or `filterable`: the parent dashboard owns the search box.
|
|
61
|
+
Table pages the whole filtered list the parent passes, and stays mounted
|
|
62
|
+
while it loads.
|
|
63
|
+
--}}
|
|
64
|
+
<Table
|
|
65
|
+
:page-reset-key="pageResetKey()"
|
|
66
|
+
:columns="COLUMNS"
|
|
67
|
+
:data="rows()"
|
|
68
|
+
:loading="loading()"
|
|
69
|
+
:row-actions="ROW_ACTIONS"
|
|
70
|
+
action-label-key="name"
|
|
71
|
+
paginated
|
|
72
|
+
:page-size="10"
|
|
73
|
+
empty-title="No shipping methods found"
|
|
74
|
+
empty-description="Add a method, or adjust the search."
|
|
75
|
+
@row-action="handleRowAction"
|
|
76
|
+
/>
|
|
@@ -44,8 +44,6 @@ const {
|
|
|
44
44
|
loading,
|
|
45
45
|
loadError,
|
|
46
46
|
search,
|
|
47
|
-
page,
|
|
48
|
-
perPage,
|
|
49
47
|
editorOpen,
|
|
50
48
|
editorMode,
|
|
51
49
|
editing,
|
|
@@ -55,8 +53,6 @@ const {
|
|
|
55
53
|
deleteBusy,
|
|
56
54
|
deleteError,
|
|
57
55
|
filteredRecords,
|
|
58
|
-
paginatedRecords,
|
|
59
|
-
goToPage,
|
|
60
56
|
handleSearch,
|
|
61
57
|
openCreate,
|
|
62
58
|
openEdit,
|
|
@@ -65,7 +61,6 @@ const {
|
|
|
65
61
|
closeDelete,
|
|
66
62
|
upsert,
|
|
67
63
|
remove,
|
|
68
|
-
clampPage,
|
|
69
64
|
} = useRecordsDashboard<ShippingRateRecord>({ filter: matchesQuery })
|
|
70
65
|
|
|
71
66
|
const methods = state<ShippingResourceOption[]>([])
|
|
@@ -122,7 +117,6 @@ async function loadRates(): Promise<void> {
|
|
|
122
117
|
methods.set(methodData)
|
|
123
118
|
zones.set(zoneData)
|
|
124
119
|
records.set(rateData.map(withRelations))
|
|
125
|
-
clampPage()
|
|
126
120
|
}
|
|
127
121
|
catch (error) {
|
|
128
122
|
loadError.set(error instanceof Error ? error.message : String(error))
|
|
@@ -179,7 +173,6 @@ async function confirmDelete(): Promise<void> {
|
|
|
179
173
|
})
|
|
180
174
|
remove(current)
|
|
181
175
|
deleting.set(null)
|
|
182
|
-
clampPage()
|
|
183
176
|
pushToast('success', 'Shipping rate deleted', {
|
|
184
177
|
detail: `${current.shipping_method?.name || 'Method'} for ${current.shipping_zone?.name || 'zone'}`,
|
|
185
178
|
})
|
|
@@ -217,11 +210,7 @@ onMount(() => {
|
|
|
217
210
|
<div><h2 class="font-semibold text-gray-900 dark:text-white">Configured weight bands</h2><p class="mt-0.5 text-gray-500 text-xs dark:text-neutral-400">{{ filteredRecords().length }} matching records</p></div>
|
|
218
211
|
<SearchFilter placeholder="Search shipping rates" class="w-full sm:w-80" @search="handleSearch" />
|
|
219
212
|
</div>
|
|
220
|
-
<
|
|
221
|
-
<ShippingRatesTable :if="!loading()" :rates="paginatedRecords()" @edit="openEdit" @delete="requestDelete" />
|
|
222
|
-
<div :if="!loading()" class="px-4 py-3 bg-gray-50 dark:bg-neutral-900/50 border-gray-200 border-t dark:border-neutral-700">
|
|
223
|
-
<DeliveryPagination :current-page="page()" :total-items="filteredRecords().length" :items-per-page="perPage()" @select="goToPage" />
|
|
224
|
-
</div>
|
|
213
|
+
<ShippingRatesTable :rates="filteredRecords()" :loading="loading()" :page-reset-key="search()" @edit="openEdit" @delete="requestDelete" />
|
|
225
214
|
</section>
|
|
226
215
|
</div>
|
|
227
216
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
@import('../../UI/Table')
|
|
2
|
+
|
|
1
3
|
<script client>
|
|
2
|
-
import { formatCurrency as formatMinorUnits } from '@stacksjs/commerce/money'
|
|
3
4
|
interface ResourceSummary {
|
|
4
5
|
id: number
|
|
5
6
|
name: string
|
|
@@ -17,84 +18,70 @@ interface ShippingRateRow {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
const rates = useReactiveProp('rates', [] as ShippingRateRow[])
|
|
21
|
+
const loading = useReactiveProp('loading', false)
|
|
22
|
+
/** The parent's search: a change returns to the first page. */
|
|
23
|
+
const pageResetKey = useReactiveProp<unknown>('pageResetKey', '')
|
|
20
24
|
const emit = defineEmits()
|
|
21
25
|
|
|
22
|
-
// Shipping amounts are stored as integer cents, in USD.
|
|
23
|
-
function formatCurrency(cents: number): string {
|
|
24
|
-
return formatMinorUnits(cents, 'USD', 'en-US')
|
|
25
|
-
}
|
|
26
|
-
|
|
27
26
|
function formatWeight(weight: number): string {
|
|
28
27
|
return `${new Intl.NumberFormat('en-US', { maximumFractionDigits: 2 }).format(weight)} kg`
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Shipping amounts are stored as integer cents, in USD, so the rate is a
|
|
32
|
+
* currency column with that code. Weights carry their unit and sort by the number.
|
|
33
|
+
*/
|
|
34
|
+
const COLUMNS = [
|
|
35
|
+
{ key: 'methodLabel', label: 'Method', sortable: true, class: 'font-medium text-stone-800 whitespace-nowrap dark:text-white' },
|
|
36
|
+
{ key: 'zoneLabel', label: 'Zone', sortable: true, class: 'whitespace-nowrap' },
|
|
37
|
+
{ key: 'weight_from', label: 'From', sortable: true, slotted: true },
|
|
38
|
+
{ key: 'weight_to', label: 'To', sortable: true, slotted: true },
|
|
39
|
+
{ key: 'rate', label: 'Rate', type: 'currency' as const, currency: 'USD', sortable: true, class: 'font-medium tabular-nums text-stone-800 whitespace-nowrap dark:text-white' },
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
const ROW_ACTIONS = [
|
|
43
|
+
{ action: 'edit', label: 'Edit', icon: 'i-hugeicons-edit-01' },
|
|
44
|
+
{ action: 'delete', label: 'Delete', icon: 'i-hugeicons-waste', class: 'text-red-500' },
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
const rows = derived(() => rates().map((rate) => {
|
|
32
48
|
const method = rate.shipping_method?.name || `method ${rate.shipping_method_id}`
|
|
33
49
|
const zone = rate.shipping_zone?.name || `zone ${rate.shipping_zone_id}`
|
|
34
|
-
return
|
|
50
|
+
return {
|
|
51
|
+
...rate,
|
|
52
|
+
methodLabel: rate.shipping_method?.name || `Method #${rate.shipping_method_id}`,
|
|
53
|
+
zoneLabel: rate.shipping_zone?.name || `Zone #${rate.shipping_zone_id}`,
|
|
54
|
+
fromLabel: formatWeight(rate.weight_from),
|
|
55
|
+
toLabel: formatWeight(rate.weight_to),
|
|
56
|
+
actionName: `${method} rate for ${zone}, ${formatWeight(rate.weight_from)} to ${formatWeight(rate.weight_to)}`,
|
|
57
|
+
}
|
|
58
|
+
}))
|
|
59
|
+
|
|
60
|
+
function handleRowAction(payload: { action: string, row: ShippingRateRow }): void {
|
|
61
|
+
emit(payload.action, payload.row)
|
|
35
62
|
}
|
|
36
63
|
</script>
|
|
37
64
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
<td class="px-4 py-3.5 tabular-nums text-sm text-stone-500 whitespace-nowrap dark:text-neutral-300">
|
|
62
|
-
{{ formatWeight(rate.weight_from) }}
|
|
63
|
-
</td>
|
|
64
|
-
<td class="px-4 py-3.5 tabular-nums text-sm text-stone-500 whitespace-nowrap dark:text-neutral-300">
|
|
65
|
-
{{ formatWeight(rate.weight_to) }}
|
|
66
|
-
</td>
|
|
67
|
-
<td class="px-4 py-3.5 font-medium tabular-nums text-sm text-stone-800 whitespace-nowrap dark:text-white">
|
|
68
|
-
{{ formatCurrency(rate.rate) }}
|
|
69
|
-
</td>
|
|
70
|
-
<td class="relative pl-3 pr-6 py-3.5 font-medium text-right text-sm whitespace-nowrap">
|
|
71
|
-
<Button
|
|
72
|
-
variant="ghost"
|
|
73
|
-
size="sm"
|
|
74
|
-
iconOnly
|
|
75
|
-
@click="emit('edit', rate)"
|
|
76
|
-
:ariaLabel="actionLabel('Edit', rate)"
|
|
77
|
-
>
|
|
78
|
-
<span class="block h-4 w-4 text-blue-500 i-hugeicons-edit-01"></span>
|
|
79
|
-
</Button>
|
|
80
|
-
<Button
|
|
81
|
-
variant="ghost"
|
|
82
|
-
size="sm"
|
|
83
|
-
iconOnly
|
|
84
|
-
@click="emit('delete', rate)"
|
|
85
|
-
:ariaLabel="actionLabel('Delete', rate)"
|
|
86
|
-
>
|
|
87
|
-
<span class="block h-4 w-4 text-red-500 i-hugeicons-waste"></span>
|
|
88
|
-
</Button>
|
|
89
|
-
</td>
|
|
90
|
-
</tr>
|
|
91
|
-
</template>
|
|
92
|
-
|
|
93
|
-
<tr :if="rates().length === 0">
|
|
94
|
-
<td colspan="6" class="px-6 py-12 text-center text-sm text-stone-500 dark:text-neutral-400">
|
|
95
|
-
No shipping rates found.
|
|
96
|
-
</td>
|
|
97
|
-
</tr>
|
|
98
|
-
</tbody>
|
|
99
|
-
</table>
|
|
100
|
-
</div>
|
|
65
|
+
{{--
|
|
66
|
+
No `searchable` or `filterable`: the parent dashboard owns the search box.
|
|
67
|
+
Table pages the whole filtered list the parent passes, and stays mounted
|
|
68
|
+
while it loads.
|
|
69
|
+
--}}
|
|
70
|
+
<Table
|
|
71
|
+
:page-reset-key="pageResetKey()"
|
|
72
|
+
:columns="COLUMNS"
|
|
73
|
+
:data="rows()"
|
|
74
|
+
:loading="loading()"
|
|
75
|
+
:row-actions="ROW_ACTIONS"
|
|
76
|
+
action-label-key="actionName"
|
|
77
|
+
paginated
|
|
78
|
+
:page-size="10"
|
|
79
|
+
empty-title="No shipping rates found"
|
|
80
|
+
empty-description="Add a rate, or adjust the search."
|
|
81
|
+
@row-action="handleRowAction"
|
|
82
|
+
>
|
|
83
|
+
<template #cell="{ row, column }">
|
|
84
|
+
<span :if="column.key === 'weight_from'" class="tabular-nums whitespace-nowrap" x-text="row.fromLabel"></span>
|
|
85
|
+
<span :if="column.key === 'weight_to'" class="tabular-nums whitespace-nowrap" x-text="row.toLabel"></span>
|
|
86
|
+
</template>
|
|
87
|
+
</Table>
|
|
@@ -47,8 +47,6 @@ const {
|
|
|
47
47
|
loading,
|
|
48
48
|
loadError,
|
|
49
49
|
search,
|
|
50
|
-
page,
|
|
51
|
-
perPage,
|
|
52
50
|
editorOpen,
|
|
53
51
|
editorMode,
|
|
54
52
|
editing,
|
|
@@ -58,8 +56,6 @@ const {
|
|
|
58
56
|
deleteBusy,
|
|
59
57
|
deleteError,
|
|
60
58
|
filteredRecords,
|
|
61
|
-
paginatedRecords,
|
|
62
|
-
goToPage,
|
|
63
59
|
handleSearch,
|
|
64
60
|
openCreate,
|
|
65
61
|
openEdit,
|
|
@@ -68,7 +64,6 @@ const {
|
|
|
68
64
|
closeDelete,
|
|
69
65
|
upsert,
|
|
70
66
|
remove,
|
|
71
|
-
clampPage,
|
|
72
67
|
} = useRecordsDashboard<ShippingZoneRecord>({ filter: matchesQuery })
|
|
73
68
|
|
|
74
69
|
const methods = state<ShippingMethodOption[]>([])
|
|
@@ -130,7 +125,6 @@ async function loadZones(): Promise<void> {
|
|
|
130
125
|
throw new TypeError('Shipping zone resources were not returned as arrays.')
|
|
131
126
|
methods.set(methodData)
|
|
132
127
|
records.set(zoneData.map(withMethod))
|
|
133
|
-
clampPage()
|
|
134
128
|
}
|
|
135
129
|
catch (error) {
|
|
136
130
|
loadError.set(error instanceof Error ? error.message : String(error))
|
|
@@ -187,7 +181,6 @@ async function confirmDelete(): Promise<void> {
|
|
|
187
181
|
})
|
|
188
182
|
remove(current)
|
|
189
183
|
deleting.set(null)
|
|
190
|
-
clampPage()
|
|
191
184
|
pushToast('success', 'Shipping zone deleted', { detail: current.name })
|
|
192
185
|
}
|
|
193
186
|
catch (error) {
|
|
@@ -223,11 +216,7 @@ onMount(() => {
|
|
|
223
216
|
<div><h2 class="font-semibold text-gray-900 dark:text-white">Configured coverage</h2><p class="mt-0.5 text-gray-500 text-xs dark:text-neutral-400">{{ filteredRecords().length }} matching records</p></div>
|
|
224
217
|
<SearchFilter placeholder="Search shipping zones" class="w-full sm:w-80" @search="handleSearch" />
|
|
225
218
|
</div>
|
|
226
|
-
<
|
|
227
|
-
<ShippingZonesTable :if="!loading()" :zones="paginatedRecords()" @edit="openEdit" @delete="requestDelete" />
|
|
228
|
-
<div :if="!loading()" class="px-4 py-3 bg-gray-50 dark:bg-neutral-900/50 border-gray-200 border-t dark:border-neutral-700">
|
|
229
|
-
<DeliveryPagination :current-page="page()" :total-items="filteredRecords().length" :items-per-page="perPage()" @select="goToPage" />
|
|
230
|
-
</div>
|
|
219
|
+
<ShippingZonesTable :zones="filteredRecords()" :loading="loading()" :page-reset-key="search()" @edit="openEdit" @delete="requestDelete" />
|
|
231
220
|
</section>
|
|
232
221
|
</div>
|
|
233
222
|
|