@stacksjs/defaults 0.75.231 → 0.75.236
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/ai/skills/stacks-dashboard/SKILL.md +2 -2
- package/ai/skills/stacks-database/SKILL.md +1 -1
- package/ai/skills/stacks-deploy/SKILL.md +3 -2
- package/ai/skills/stacks-migrations/SKILL.md +2 -2
- package/ai/skills/stacks-native/CATALOG.md +10 -10
- package/ai/skills/stacks-realtime/SKILL.md +4 -2
- package/ai/skills/stacks-storage/SKILL.md +43 -0
- package/ai/skills/stacks-testing/SKILL.md +38 -10
- package/app/Actions/Monitoring/error-provider.ts +129 -12
- package/app/Models/Error.ts +21 -0
- package/functions/dashboard-form.test.ts +41 -0
- package/functions/dashboard-form.ts +30 -0
- package/functions/dashboard-list.test.ts +228 -0
- package/functions/dashboard-list.ts +257 -0
- package/ide/vscode/package.json +1 -1
- package/package.json +2 -2
- package/project/storage/framework/types/browser-composables.d.ts +56 -0
- package/resources/components/Dashboard/Auth/AuthCard.stx +29 -0
- package/resources/components/Dashboard/Auth/ForgotPassword.stx +65 -79
- package/resources/components/Dashboard/Auth/Login.stx +73 -82
- package/resources/components/Dashboard/Auth/Register.stx +121 -135
- package/resources/components/Dashboard/Auth/ResetPassword.stx +88 -102
- package/resources/components/Dashboard/Buddy/BuddyAssistant.stx +11 -15
- package/resources/components/Dashboard/Commerce/CommerceCategoriesDashboard.stx +10 -27
- package/resources/components/Dashboard/Commerce/CommerceCategoryDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceCouponDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceCouponsDashboard.stx +10 -27
- package/resources/components/Dashboard/Commerce/CommerceCustomerDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceCustomersDashboard.stx +10 -27
- package/resources/components/Dashboard/Commerce/CommerceGiftCardDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceGiftCardsDashboard.stx +10 -27
- package/resources/components/Dashboard/Commerce/CommerceManufacturerDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceManufacturersDashboard.stx +13 -24
- package/resources/components/Dashboard/Commerce/CommerceOrderDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceOrdersDashboard.stx +10 -27
- package/resources/components/Dashboard/Commerce/CommerceOverviewDashboard.stx +17 -21
- package/resources/components/Dashboard/Commerce/CommercePaymentsDashboard.stx +7 -19
- package/resources/components/Dashboard/Commerce/CommercePosDashboard.stx +4 -16
- package/resources/components/Dashboard/Commerce/CommerceProductDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceProductsDashboard.stx +10 -27
- package/resources/components/Dashboard/Commerce/CommerceReviewEditDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceReviewsDashboard.stx +7 -18
- package/resources/components/Dashboard/Commerce/CommerceTaxDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceTaxesDashboard.stx +13 -24
- package/resources/components/Dashboard/Commerce/CommerceUnitDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceUnitsDashboard.stx +13 -24
- package/resources/components/Dashboard/Commerce/CommerceVariantDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceVariantsDashboard.stx +7 -18
- package/resources/components/Dashboard/Commerce/Delivery/CouriersDashboard.stx +52 -105
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryOverviewDashboard.stx +12 -16
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryRoutesDashboard.stx +53 -105
- package/resources/components/Dashboard/Commerce/Delivery/DigitalDeliveriesDashboard.stx +7 -13
- package/resources/components/Dashboard/Commerce/Delivery/LicenseKeysDashboard.stx +53 -105
- package/resources/components/Dashboard/Commerce/Delivery/ShippingMethodsDashboard.stx +7 -13
- package/resources/components/Dashboard/Commerce/Delivery/ShippingRatesDashboard.stx +51 -103
- package/resources/components/Dashboard/Commerce/Delivery/ShippingZonesDashboard.stx +52 -104
- package/resources/components/Dashboard/Commerce/PrintDeviceDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/PrintDevicesDashboard.stx +10 -27
- package/resources/components/Dashboard/Commerce/PrintLogsDashboard.stx +7 -19
- package/resources/components/Dashboard/Commerce/ProductWaitlistDashboard.stx +12 -16
- package/resources/components/Dashboard/Commerce/ProductWaitlistEntryDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/RestaurantWaitlistDashboard.stx +12 -16
- package/resources/components/Dashboard/Commerce/RestaurantWaitlistEntryDialog.stx +2 -4
- package/resources/components/Dashboard/Deployments/DeploymentList.stx +17 -21
- package/resources/components/Dashboard/Marketing/AbandonedCartsDashboard.stx +8 -19
- package/resources/components/Dashboard/Marketing/CampaignsDashboard.stx +8 -19
- package/resources/components/Dashboard/Marketing/MarketingListsDashboard.stx +11 -22
- package/resources/components/Dashboard/Marketing/SocialPostsDashboard.stx +8 -19
- package/resources/components/Dashboard/Queue/QueueDashboard.stx +12 -16
- package/resources/components/Dashboard/Realtime/RealtimeDashboard.stx +6 -10
- package/resources/components/Dashboard/Releases/ReleaseDashboard.stx +6 -10
- package/resources/components/Dashboard/UI/MetricCards.stx +32 -0
- package/resources/components/Dashboard/UI/PageHeader.stx +25 -66
- package/resources/components/Dashboard/UI/Table.stx +79 -6
- package/resources/plugins/preloader.ts +9 -1
|
@@ -175,33 +175,22 @@ onMount(() => {
|
|
|
175
175
|
</script>
|
|
176
176
|
|
|
177
177
|
<div class="space-y-6">
|
|
178
|
-
<
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
<
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
Add manufacturer
|
|
191
|
-
</Button>
|
|
192
|
-
</div>
|
|
193
|
-
</header>
|
|
178
|
+
<PageHeader title="Manufacturers" description="Manage native Manufacturer records and their product relationships.">
|
|
179
|
+
<div class="flex gap-2">
|
|
180
|
+
<Button variant="secondary" :loading="loading()" @click="loadManufacturers">
|
|
181
|
+
<span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>
|
|
182
|
+
Refresh
|
|
183
|
+
</Button>
|
|
184
|
+
<Button @click="openAdd()">
|
|
185
|
+
<span class="h-4 w-4 i-hugeicons-add-01"></span>
|
|
186
|
+
Add manufacturer
|
|
187
|
+
</Button>
|
|
188
|
+
</div>
|
|
189
|
+
</PageHeader>
|
|
194
190
|
|
|
195
191
|
<div :if="loadError()" class="p-4 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950/30 border border-red-200 rounded-lg dark:border-red-900">{{ loadError() }}</div>
|
|
196
192
|
|
|
197
|
-
<
|
|
198
|
-
<template :for="item in metricCards()" :key="item.label">
|
|
199
|
-
<article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
200
|
-
<p class="text-gray-500 text-xs dark:text-neutral-400">{{ item.label }}</p>
|
|
201
|
-
<p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ item.value }}</p>
|
|
202
|
-
</article>
|
|
203
|
-
</template>
|
|
204
|
-
</section>
|
|
193
|
+
<MetricCards label="Manufacturer metrics" columns="grid-cols-2 lg:grid-cols-4" :items="metricCards()" />
|
|
205
194
|
|
|
206
195
|
<section class="space-y-3">
|
|
207
196
|
<div class="grid gap-3 grid-cols-1 sm:grid-cols-2 xl:grid-cols-4">
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
OrderCustomerOption,
|
|
5
5
|
} from '../../../../app/Actions/Dashboard/Commerce/commerce-order-records'
|
|
6
6
|
import { minorToInput, moneyInputError, moneyInputStep, parseMoneyInput } from '@stacksjs/commerce/money'
|
|
7
|
+
import { formErrorMessages } from '../../../../functions/dashboard-form'
|
|
7
8
|
|
|
8
9
|
const open = useReactiveProp('open', false)
|
|
9
10
|
const mode = useReactiveProp('mode', 'add')
|
|
@@ -108,10 +109,7 @@ function submit(): void {
|
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
function errorMessages(): string[] {
|
|
111
|
-
return
|
|
112
|
-
const fieldLabel = field.replace(/([a-z])([A-Z])/g, '$1 $2').replaceAll('_', ' ')
|
|
113
|
-
return `${fieldLabel.charAt(0).toUpperCase()}${fieldLabel.slice(1)}: ${message}`
|
|
114
|
-
})
|
|
112
|
+
return formErrorMessages(errors())
|
|
115
113
|
}
|
|
116
114
|
</script>
|
|
117
115
|
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
import { DashboardApiError, dashboardApi } from '../../../../functions/dashboard-api'
|
|
8
8
|
import { pushToast } from '../../../../functions/toasts'
|
|
9
9
|
import { formatCurrency, minorToInput } from '@stacksjs/commerce/money'
|
|
10
|
+
import { pageControls } from '../../../../functions/dashboard-list'
|
|
10
11
|
|
|
11
12
|
interface OrderIndexResponse {
|
|
12
13
|
records: CommerceOrderRecord[]
|
|
@@ -284,13 +285,7 @@ async function deleteOrder(): Promise<void> {
|
|
|
284
285
|
}
|
|
285
286
|
}
|
|
286
287
|
|
|
287
|
-
|
|
288
|
-
page.set(Math.max(1, page() - 1))
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function nextPage(): void {
|
|
292
|
-
page.set(Math.min(pageCount(), page() + 1))
|
|
293
|
-
}
|
|
288
|
+
const { previousPage, nextPage } = pageControls(page, pageCount)
|
|
294
289
|
|
|
295
290
|
onMount(() => {
|
|
296
291
|
void loadOrders()
|
|
@@ -298,29 +293,17 @@ onMount(() => {
|
|
|
298
293
|
</script>
|
|
299
294
|
|
|
300
295
|
<div class="space-y-6">
|
|
301
|
-
<
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
</
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
<Button tag="a" variant="secondary" :href="exportHref()" :download="'orders-' + new Date().toISOString().slice(0, 10) + '.csv'"><span class="h-4 w-4 i-hugeicons-download-04"></span>Export filtered</Button>
|
|
309
|
-
<Button @click="openAdd()"><span class="h-4 w-4 i-hugeicons-add-01"></span>Add order</Button>
|
|
310
|
-
</div>
|
|
311
|
-
</header>
|
|
296
|
+
<PageHeader title="Orders" description="Manage native Order records, customer assignments, totals, and fulfillment state.">
|
|
297
|
+
<div class="flex flex-wrap gap-2">
|
|
298
|
+
<Button variant="secondary" :loading="loading()" @click="loadOrders()"><span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>Refresh</Button>
|
|
299
|
+
<Button tag="a" variant="secondary" :href="exportHref()" :download="'orders-' + new Date().toISOString().slice(0, 10) + '.csv'"><span class="h-4 w-4 i-hugeicons-download-04"></span>Export filtered</Button>
|
|
300
|
+
<Button @click="openAdd()"><span class="h-4 w-4 i-hugeicons-add-01"></span>Add order</Button>
|
|
301
|
+
</div>
|
|
302
|
+
</PageHeader>
|
|
312
303
|
|
|
313
304
|
<div :if="loadError()" class="p-4 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950/30 border border-red-200 rounded-lg dark:border-red-900">{{ loadError() }}</div>
|
|
314
305
|
|
|
315
|
-
<
|
|
316
|
-
<template :for="item in metricCards()" :key="item.label">
|
|
317
|
-
<article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
318
|
-
<p class="text-gray-500 text-xs dark:text-neutral-400">{{ item.label }}</p>
|
|
319
|
-
<p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ item.value }}</p>
|
|
320
|
-
<p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">{{ item.detail }}</p>
|
|
321
|
-
</article>
|
|
322
|
-
</template>
|
|
323
|
-
</section>
|
|
306
|
+
<MetricCards label="Order metrics" columns="grid-cols-2 lg:grid-cols-5" :items="metricCards()" />
|
|
324
307
|
|
|
325
308
|
<section :if="summary().currencyTotals.length > 0" aria-label="Order currency totals" class="flex flex-wrap gap-2 items-center">
|
|
326
309
|
<span class="text-gray-500 text-xs dark:text-neutral-400">Recorded order value</span>
|
|
@@ -62,27 +62,23 @@ onMount(() => {
|
|
|
62
62
|
</script>
|
|
63
63
|
|
|
64
64
|
<div class="space-y-8">
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
</
|
|
75
|
-
<
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
<option value="all">All time</option>
|
|
83
|
-
</select>
|
|
84
|
-
</div>
|
|
85
|
-
</header>
|
|
65
|
+
<PageHeader title="Commerce Dashboard" :description="`Persisted order performance for ${rangeLabel().toLowerCase()}.`">
|
|
66
|
+
<div class="flex gap-2">
|
|
67
|
+
<Button variant="secondary" :loading="loading()" @click="loadDashboard">
|
|
68
|
+
<span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>
|
|
69
|
+
Refresh
|
|
70
|
+
</Button>
|
|
71
|
+
<label class="sr-only" for="commerce-dashboard-range">Reporting range</label>
|
|
72
|
+
<select id="commerce-dashboard-range" x-model="range" class="block pl-3 pr-10 py-2 text-gray-900 sm:leading-6 sm:text-sm dark:text-white dark:bg-neutral-800 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-700 focus:ring-2 focus:ring-blue-600" @change="reloadSelectedRange($event)">
|
|
73
|
+
<option value="today">Today</option>
|
|
74
|
+
<option value="7d">Last 7 days</option>
|
|
75
|
+
<option value="30d">Last 30 days</option>
|
|
76
|
+
<option value="90d">Last 90 days</option>
|
|
77
|
+
<option value="year">Last year</option>
|
|
78
|
+
<option value="all">All time</option>
|
|
79
|
+
</select>
|
|
80
|
+
</div>
|
|
81
|
+
</PageHeader>
|
|
86
82
|
|
|
87
83
|
<div :if="loadError()" class="px-4 py-3 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950/30 border border-red-300 rounded-md dark:border-red-800">
|
|
88
84
|
{{ loadError() }}
|
|
@@ -170,28 +170,16 @@ onMount(() => {
|
|
|
170
170
|
</script>
|
|
171
171
|
|
|
172
172
|
<div class="space-y-6">
|
|
173
|
-
<
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
<Button variant="secondary" :loading="loading()" @click="loadPayments"><span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>Refresh</Button>
|
|
180
|
-
<Button tag="a" :href="exportHref()" :download="exportFilename()"><span class="h-4 w-4 i-hugeicons-download-04"></span>Export current view</Button>
|
|
181
|
-
</div>
|
|
182
|
-
</header>
|
|
173
|
+
<PageHeader title="Payments" description="Inspect native Payment records, export the current view, and record confirmed refunds.">
|
|
174
|
+
<div class="flex flex-wrap gap-2">
|
|
175
|
+
<Button variant="secondary" :loading="loading()" @click="loadPayments"><span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>Refresh</Button>
|
|
176
|
+
<Button tag="a" :href="exportHref()" :download="exportFilename()"><span class="h-4 w-4 i-hugeicons-download-04"></span>Export current view</Button>
|
|
177
|
+
</div>
|
|
178
|
+
</PageHeader>
|
|
183
179
|
|
|
184
180
|
<div :if="loadError()" class="p-4 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950/30 border border-red-200 rounded-lg dark:border-red-900">{{ loadError() }}</div>
|
|
185
181
|
|
|
186
|
-
<
|
|
187
|
-
<template :for="item in metricCards()" :key="item.label">
|
|
188
|
-
<article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
189
|
-
<p class="text-gray-500 text-xs dark:text-neutral-400">{{ item.label }}</p>
|
|
190
|
-
<p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ item.value }}</p>
|
|
191
|
-
<p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">{{ item.detail }}</p>
|
|
192
|
-
</article>
|
|
193
|
-
</template>
|
|
194
|
-
</section>
|
|
182
|
+
<MetricCards label="Payment metrics" columns="grid-cols-2 lg:grid-cols-4" :items="metricCards()" />
|
|
195
183
|
|
|
196
184
|
<CommercePaymentsBreakdown :records="visibleRecords()" />
|
|
197
185
|
|
|
@@ -314,25 +314,13 @@ onMount(() => {
|
|
|
314
314
|
</script>
|
|
315
315
|
|
|
316
316
|
<div class="space-y-6">
|
|
317
|
-
<
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Create server-priced cash sales from native Product, Order, OrderItem, Payment, Customer, and TaxRate records.</p>
|
|
321
|
-
</div>
|
|
322
|
-
<Button variant="secondary" :loading="loading()" @click="loadPos()"><span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>Refresh</Button>
|
|
323
|
-
</header>
|
|
317
|
+
<PageHeader title="Point of sale" description="Create server-priced cash sales from native Product, Order, OrderItem, Payment, Customer, and TaxRate records.">
|
|
318
|
+
<Button variant="secondary" :loading="loading()" @click="loadPos()"><span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>Refresh</Button>
|
|
319
|
+
</PageHeader>
|
|
324
320
|
|
|
325
321
|
<div :if="loadError()" class="p-4 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950/30 border border-red-200 rounded-lg dark:border-red-900">{{ loadError() }}</div>
|
|
326
322
|
|
|
327
|
-
<
|
|
328
|
-
<template :for="item in metricCards()" :key="item.label">
|
|
329
|
-
<article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
330
|
-
<p class="text-gray-500 text-xs dark:text-neutral-400">{{ item.label }}</p>
|
|
331
|
-
<p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ item.value }}</p>
|
|
332
|
-
<p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">{{ item.detail }}</p>
|
|
333
|
-
</article>
|
|
334
|
-
</template>
|
|
335
|
-
</section>
|
|
323
|
+
<MetricCards label="Point of sale metrics" columns="grid-cols-2 xl:grid-cols-4" :items="metricCards()" />
|
|
336
324
|
|
|
337
325
|
<div class="grid gap-6 grid-cols-1 items-start lg:grid-cols-[minmax(0,1fr)_20rem]">
|
|
338
326
|
<CommercePosCatalog :products="visibleProducts()" :categories="categories()" :search="search()" :category="categoryFilter()" :availability="availabilityFilter()" :currency="defaultCurrency()" :loading="loading()" @search="search.set($event)" @category="categoryFilter.set($event)" @availability="availabilityFilter.set($event)" @add="addProduct" />
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script client>
|
|
2
|
+
import { formErrorMessages } from '../../../../functions/dashboard-form'
|
|
2
3
|
import type {
|
|
3
4
|
CommerceProductRecord,
|
|
4
5
|
ProductOption,
|
|
@@ -108,10 +109,7 @@ function submit(): void {
|
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
function errorMessages(): string[] {
|
|
111
|
-
return
|
|
112
|
-
const fieldLabel = field.replace(/([a-z])([A-Z])/g, '$1 $2').replaceAll('_', ' ')
|
|
113
|
-
return `${fieldLabel.charAt(0).toUpperCase()}${fieldLabel.slice(1)}: ${message}`
|
|
114
|
-
})
|
|
112
|
+
return formErrorMessages(errors())
|
|
115
113
|
}
|
|
116
114
|
</script>
|
|
117
115
|
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
import { DashboardApiError, dashboardApi } from '../../../../functions/dashboard-api'
|
|
9
9
|
import { pushToast } from '../../../../functions/toasts'
|
|
10
10
|
import { minorToInput } from '@stacksjs/commerce/money'
|
|
11
|
+
import { pageControls } from '../../../../functions/dashboard-list'
|
|
11
12
|
|
|
12
13
|
interface ProductIndexResponse {
|
|
13
14
|
records: CommerceProductRecord[]
|
|
@@ -259,13 +260,7 @@ async function deleteProduct(): Promise<void> {
|
|
|
259
260
|
}
|
|
260
261
|
}
|
|
261
262
|
|
|
262
|
-
|
|
263
|
-
page.set(Math.max(1, page() - 1))
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function nextPage(): void {
|
|
267
|
-
page.set(Math.min(pageCount(), page() + 1))
|
|
268
|
-
}
|
|
263
|
+
const { previousPage, nextPage } = pageControls(page, pageCount)
|
|
269
264
|
|
|
270
265
|
onMount(() => {
|
|
271
266
|
void loadProducts()
|
|
@@ -273,29 +268,17 @@ onMount(() => {
|
|
|
273
268
|
</script>
|
|
274
269
|
|
|
275
270
|
<div class="space-y-6">
|
|
276
|
-
<
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
</
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
<Button tag="a" variant="secondary" :href="exportHref()" :download="'products-' + new Date().toISOString().slice(0, 10) + '.csv'"><span class="h-4 w-4 i-hugeicons-download-04"></span>Export filtered</Button>
|
|
284
|
-
<Button @click="openAdd()"><span class="h-4 w-4 i-hugeicons-add-01"></span>Add product</Button>
|
|
285
|
-
</div>
|
|
286
|
-
</header>
|
|
271
|
+
<PageHeader title="Products" description="Manage native Product records, commerce availability, inventory, and relationships.">
|
|
272
|
+
<div class="flex flex-wrap gap-2">
|
|
273
|
+
<Button variant="secondary" :loading="loading()" @click="loadProducts()"><span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>Refresh</Button>
|
|
274
|
+
<Button tag="a" variant="secondary" :href="exportHref()" :download="'products-' + new Date().toISOString().slice(0, 10) + '.csv'"><span class="h-4 w-4 i-hugeicons-download-04"></span>Export filtered</Button>
|
|
275
|
+
<Button @click="openAdd()"><span class="h-4 w-4 i-hugeicons-add-01"></span>Add product</Button>
|
|
276
|
+
</div>
|
|
277
|
+
</PageHeader>
|
|
287
278
|
|
|
288
279
|
<div :if="loadError()" class="p-4 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950/30 border border-red-200 rounded-lg dark:border-red-900">{{ loadError() }}</div>
|
|
289
280
|
|
|
290
|
-
<
|
|
291
|
-
<template :for="item in metricCards()" :key="item.label">
|
|
292
|
-
<article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
293
|
-
<p class="text-gray-500 text-xs dark:text-neutral-400">{{ item.label }}</p>
|
|
294
|
-
<p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ item.value }}</p>
|
|
295
|
-
<p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">{{ item.detail }}</p>
|
|
296
|
-
</article>
|
|
297
|
-
</template>
|
|
298
|
-
</section>
|
|
281
|
+
<MetricCards label="Product metrics" columns="grid-cols-2 lg:grid-cols-5" :items="metricCards()" />
|
|
299
282
|
|
|
300
283
|
<section class="space-y-3">
|
|
301
284
|
<div class="grid gap-3 grid-cols-1 sm:grid-cols-2 lg:grid-cols-5">
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script client>
|
|
2
2
|
import type { ReviewRecord } from '../../../../app/Actions/Dashboard/Commerce/review-records'
|
|
3
|
+
import { formErrorMessages } from '../../../../functions/dashboard-form'
|
|
3
4
|
|
|
4
5
|
const open = useReactiveProp('open', false)
|
|
5
6
|
const review = useReactiveProp('review', null as ReviewRecord | null)
|
|
@@ -42,10 +43,7 @@ function submit(): void {
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
function errorMessages(): string[] {
|
|
45
|
-
return
|
|
46
|
-
const label = field.replace(/([a-z])([A-Z])/g, '$1 $2').replaceAll('_', ' ')
|
|
47
|
-
return `${label.charAt(0).toUpperCase()}${label.slice(1)}: ${message}`
|
|
48
|
-
})
|
|
46
|
+
return formErrorMessages(errors())
|
|
49
47
|
}
|
|
50
48
|
</script>
|
|
51
49
|
|
|
@@ -281,27 +281,16 @@ onMount(() => {
|
|
|
281
281
|
</script>
|
|
282
282
|
|
|
283
283
|
<div class="space-y-6">
|
|
284
|
-
<
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
<span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>
|
|
291
|
-
Refresh
|
|
292
|
-
</Button>
|
|
293
|
-
</header>
|
|
284
|
+
<PageHeader :title="title()" :description="description()">
|
|
285
|
+
<Button variant="secondary" :loading="loading()" @click="loadReviews">
|
|
286
|
+
<span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>
|
|
287
|
+
Refresh
|
|
288
|
+
</Button>
|
|
289
|
+
</PageHeader>
|
|
294
290
|
|
|
295
291
|
<div :if="loadError()" class="p-4 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950/30 border border-red-200 rounded-lg dark:border-red-900">{{ loadError() }}</div>
|
|
296
292
|
|
|
297
|
-
<
|
|
298
|
-
<template :for="item in metricCards()" :key="item.label">
|
|
299
|
-
<article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
300
|
-
<p class="text-gray-500 text-xs dark:text-neutral-400">{{ item.label }}</p>
|
|
301
|
-
<p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ item.value }}</p>
|
|
302
|
-
</article>
|
|
303
|
-
</template>
|
|
304
|
-
</section>
|
|
293
|
+
<MetricCards label="Review metrics" columns="grid-cols-2 lg:grid-cols-5" :items="metricCards()" />
|
|
305
294
|
|
|
306
295
|
<section class="space-y-3">
|
|
307
296
|
<div class="grid gap-3 grid-cols-1 md:grid-cols-2 xl:grid-cols-5">
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script client>
|
|
2
2
|
import type { TaxRateRecord } from '../../../../app/Actions/Dashboard/Commerce/tax-rate-records'
|
|
3
|
+
import { formErrorMessages } from '../../../../functions/dashboard-form'
|
|
3
4
|
|
|
4
5
|
const open = useReactiveProp('open', false)
|
|
5
6
|
const mode = useReactiveProp('mode', 'add')
|
|
@@ -59,10 +60,7 @@ function submit(): void {
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
function errorMessages(): string[] {
|
|
62
|
-
return
|
|
63
|
-
const label = field.replace(/([a-z])([A-Z])/g, '$1 $2').replaceAll('_', ' ')
|
|
64
|
-
return `${label.charAt(0).toUpperCase()}${label.slice(1)}: ${message}`
|
|
65
|
-
})
|
|
63
|
+
return formErrorMessages(errors())
|
|
66
64
|
}
|
|
67
65
|
</script>
|
|
68
66
|
|
|
@@ -203,22 +203,18 @@ onMount(() => {
|
|
|
203
203
|
</script>
|
|
204
204
|
|
|
205
205
|
<div class="space-y-6">
|
|
206
|
-
<
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
<
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
Add tax rate
|
|
219
|
-
</Button>
|
|
220
|
-
</div>
|
|
221
|
-
</header>
|
|
206
|
+
<PageHeader title="Tax rates" description="Manage native TaxRate records used by commerce calculations.">
|
|
207
|
+
<div class="flex gap-2">
|
|
208
|
+
<Button variant="secondary" :loading="loading()" @click="loadTaxes">
|
|
209
|
+
<span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>
|
|
210
|
+
Refresh
|
|
211
|
+
</Button>
|
|
212
|
+
<Button @click="openAdd()">
|
|
213
|
+
<span class="h-4 w-4 i-hugeicons-add-01"></span>
|
|
214
|
+
Add tax rate
|
|
215
|
+
</Button>
|
|
216
|
+
</div>
|
|
217
|
+
</PageHeader>
|
|
222
218
|
|
|
223
219
|
<div :if="loadError()" class="p-4 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950/30 border border-red-200 rounded-lg dark:border-red-900">{{ loadError() }}</div>
|
|
224
220
|
<div :if="summary().defaultConflicts > 0" class="flex gap-3 items-start p-4 text-amber-800 text-sm dark:text-amber-200 bg-amber-50 dark:bg-amber-950/30 border border-amber-200 rounded-lg dark:border-amber-900">
|
|
@@ -226,14 +222,7 @@ onMount(() => {
|
|
|
226
222
|
<p>{{ summary().defaultConflicts + 1 }} tax rates are marked as default. Save the intended default to clear the others.</p>
|
|
227
223
|
</div>
|
|
228
224
|
|
|
229
|
-
<
|
|
230
|
-
<template :for="item in metricCards()" :key="item.label">
|
|
231
|
-
<article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
232
|
-
<p class="text-gray-500 text-xs dark:text-neutral-400">{{ item.label }}</p>
|
|
233
|
-
<p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ item.value }}</p>
|
|
234
|
-
</article>
|
|
235
|
-
</template>
|
|
236
|
-
</section>
|
|
225
|
+
<MetricCards label="Tax rate metrics" columns="grid-cols-2 lg:grid-cols-4" :items="metricCards()" />
|
|
237
226
|
|
|
238
227
|
<section class="space-y-3">
|
|
239
228
|
<div class="grid gap-3 grid-cols-1 sm:grid-cols-2 xl:grid-cols-5">
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script client>
|
|
2
|
+
import { formErrorMessages } from '../../../../functions/dashboard-form'
|
|
2
3
|
import type {
|
|
3
4
|
ProductUnitOption,
|
|
4
5
|
ProductUnitRecord,
|
|
@@ -52,10 +53,7 @@ function submit(): void {
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
function errorMessages(): string[] {
|
|
55
|
-
return
|
|
56
|
-
const label = field.replace(/([a-z])([A-Z])/g, '$1 $2').replaceAll('_', ' ')
|
|
57
|
-
return `${label.charAt(0).toUpperCase()}${label.slice(1)}: ${message}`
|
|
58
|
-
})
|
|
56
|
+
return formErrorMessages(errors())
|
|
59
57
|
}
|
|
60
58
|
</script>
|
|
61
59
|
|
|
@@ -203,22 +203,18 @@ onMount(() => {
|
|
|
203
203
|
</script>
|
|
204
204
|
|
|
205
205
|
<div class="space-y-6">
|
|
206
|
-
<
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
<
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
Add unit
|
|
219
|
-
</Button>
|
|
220
|
-
</div>
|
|
221
|
-
</header>
|
|
206
|
+
<PageHeader title="Units" description="Manage native ProductUnit records and their product relationships.">
|
|
207
|
+
<div class="flex gap-2">
|
|
208
|
+
<Button variant="secondary" :loading="loading()" @click="loadUnits">
|
|
209
|
+
<span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>
|
|
210
|
+
Refresh
|
|
211
|
+
</Button>
|
|
212
|
+
<Button @click="openAdd()">
|
|
213
|
+
<span class="h-4 w-4 i-hugeicons-add-01"></span>
|
|
214
|
+
Add unit
|
|
215
|
+
</Button>
|
|
216
|
+
</div>
|
|
217
|
+
</PageHeader>
|
|
222
218
|
|
|
223
219
|
<div :if="loadError()" class="p-4 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950/30 border border-red-200 rounded-lg dark:border-red-900">{{ loadError() }}</div>
|
|
224
220
|
<div :if="summary().defaultConflicts > 0" class="flex gap-3 items-start p-4 text-amber-800 text-sm dark:text-amber-200 bg-amber-50 dark:bg-amber-950/30 border border-amber-200 rounded-lg dark:border-amber-900">
|
|
@@ -226,14 +222,7 @@ onMount(() => {
|
|
|
226
222
|
<p>{{ summary().defaultConflicts }} unit {{ summary().defaultConflicts === 1 ? 'type has' : 'types have' }} competing defaults. Save the intended default to normalize that type.</p>
|
|
227
223
|
</div>
|
|
228
224
|
|
|
229
|
-
<
|
|
230
|
-
<template :for="item in metricCards()" :key="item.label">
|
|
231
|
-
<article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
232
|
-
<p class="text-gray-500 text-xs dark:text-neutral-400">{{ item.label }}</p>
|
|
233
|
-
<p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ item.value }}</p>
|
|
234
|
-
</article>
|
|
235
|
-
</template>
|
|
236
|
-
</section>
|
|
225
|
+
<MetricCards label="Product unit metrics" columns="grid-cols-2 lg:grid-cols-4" :items="metricCards()" />
|
|
237
226
|
|
|
238
227
|
<section class="space-y-3">
|
|
239
228
|
<div class="grid gap-3 grid-cols-1 sm:grid-cols-2 xl:grid-cols-4">
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script client>
|
|
2
2
|
import type { ProductVariantOption, ProductVariantRecord } from '../../../../app/Actions/Dashboard/Commerce/product-variant-records'
|
|
3
|
+
import { formErrorMessages } from '../../../../functions/dashboard-form'
|
|
3
4
|
|
|
4
5
|
const open = useReactiveProp('open', false)
|
|
5
6
|
const mode = useReactiveProp('mode', 'add')
|
|
@@ -49,10 +50,7 @@ function submit(): void {
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
function errorMessages(): string[] {
|
|
52
|
-
return
|
|
53
|
-
const label = field.replace(/([a-z])([A-Z])/g, '$1 $2').replaceAll('_', ' ')
|
|
54
|
-
return `${label.charAt(0).toUpperCase()}${label.slice(1)}: ${message}`
|
|
55
|
-
})
|
|
53
|
+
return formErrorMessages(errors())
|
|
56
54
|
}
|
|
57
55
|
</script>
|
|
58
56
|
|
|
@@ -198,27 +198,16 @@ onMount(() => {
|
|
|
198
198
|
</script>
|
|
199
199
|
|
|
200
200
|
<div class="space-y-6">
|
|
201
|
-
<
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
<Button variant="secondary" :loading="loading()" @click="loadVariants"><span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>Refresh</Button>
|
|
208
|
-
<Button @click="openAdd()"><span class="h-4 w-4 i-hugeicons-add-01"></span>Add variant</Button>
|
|
209
|
-
</div>
|
|
210
|
-
</header>
|
|
201
|
+
<PageHeader title="Product variants" description="Manage native ProductVariant records, option sets, and product relationships.">
|
|
202
|
+
<div class="flex gap-2">
|
|
203
|
+
<Button variant="secondary" :loading="loading()" @click="loadVariants"><span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>Refresh</Button>
|
|
204
|
+
<Button @click="openAdd()"><span class="h-4 w-4 i-hugeicons-add-01"></span>Add variant</Button>
|
|
205
|
+
</div>
|
|
206
|
+
</PageHeader>
|
|
211
207
|
|
|
212
208
|
<div :if="loadError()" class="p-4 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950/30 border border-red-200 rounded-lg dark:border-red-900">{{ loadError() }}</div>
|
|
213
209
|
|
|
214
|
-
<
|
|
215
|
-
<template :for="item in metricCards()" :key="item.label">
|
|
216
|
-
<article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
217
|
-
<p class="text-gray-500 text-xs dark:text-neutral-400">{{ item.label }}</p>
|
|
218
|
-
<p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ item.value }}</p>
|
|
219
|
-
</article>
|
|
220
|
-
</template>
|
|
221
|
-
</section>
|
|
210
|
+
<MetricCards label="Product variant metrics" columns="grid-cols-2 lg:grid-cols-4" :items="metricCards()" />
|
|
222
211
|
|
|
223
212
|
<section class="space-y-3">
|
|
224
213
|
<div class="grid gap-3 grid-cols-1 sm:grid-cols-2 xl:grid-cols-5">
|