@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
|
@@ -43,121 +43,107 @@ function handleSubmit(): void {
|
|
|
43
43
|
}
|
|
44
44
|
</script>
|
|
45
45
|
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
<AuthCard :title="title()" :subtitle="subtitle()">
|
|
47
|
+
|
|
48
|
+
<!-- Form Card -->
|
|
49
|
+
<div class="p-6 bg-white dark:bg-neutral-900 border border-neutral-200/60 rounded-2xl dark:border-neutral-800 shadow-black/5 shadow-xl dark:shadow-black/20">
|
|
50
|
+
<div :if="checking()">
|
|
51
|
+
<div class="py-4 text-center">
|
|
52
|
+
<div class="inline-flex items-center justify-center mb-4 h-12 w-12 bg-neutral-100 dark:bg-neutral-800 rounded-full">
|
|
53
|
+
<div class="h-6 w-6 text-neutral-500 animate-spin i-hugeicons-loading-03"></div>
|
|
54
|
+
</div>
|
|
55
|
+
<p class="text-neutral-500 text-sm dark:text-neutral-400">Checking your link</p>
|
|
52
56
|
</div>
|
|
53
|
-
<h1 class="font-semibold text-2xl text-neutral-900 dark:text-white">
|
|
54
|
-
{{ title() }}
|
|
55
|
-
</h1>
|
|
56
|
-
<p class="mt-2 text-neutral-500 text-sm dark:text-neutral-400">
|
|
57
|
-
{{ subtitle() }}
|
|
58
|
-
</p>
|
|
59
57
|
</div>
|
|
60
58
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
<div class="inline-flex items-center justify-center mb-4 h-12 w-12 bg-neutral-100 dark:bg-neutral-800 rounded-full">
|
|
66
|
-
<div class="h-6 w-6 text-neutral-500 animate-spin i-hugeicons-loading-03"></div>
|
|
67
|
-
</div>
|
|
68
|
-
<p class="text-neutral-500 text-sm dark:text-neutral-400">Checking your link</p>
|
|
59
|
+
<div :else-if="invalid()">
|
|
60
|
+
<div class="py-4 text-center">
|
|
61
|
+
<div class="inline-flex items-center justify-center mb-4 h-12 w-12 bg-red-100 dark:bg-red-900/30 rounded-full">
|
|
62
|
+
<div class="h-6 w-6 text-red-600 dark:text-red-400 i-hugeicons-alert-circle"></div>
|
|
69
63
|
</div>
|
|
64
|
+
<h2 class="mb-2 font-medium text-lg text-neutral-900 dark:text-white">
|
|
65
|
+
This link no longer works
|
|
66
|
+
</h2>
|
|
67
|
+
<p class="mb-6 text-neutral-500 text-sm dark:text-neutral-400">
|
|
68
|
+
Reset links expire, and each one can be used only once. Ask for a fresh link and it will arrive in a moment.
|
|
69
|
+
</p>
|
|
70
|
+
<Button href="/forgot-password">
|
|
71
|
+
Send a new link
|
|
72
|
+
</Button>
|
|
70
73
|
</div>
|
|
74
|
+
</div>
|
|
71
75
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
</div>
|
|
77
|
-
<h2 class="mb-2 font-medium text-lg text-neutral-900 dark:text-white">
|
|
78
|
-
This link no longer works
|
|
79
|
-
</h2>
|
|
80
|
-
<p class="mb-6 text-neutral-500 text-sm dark:text-neutral-400">
|
|
81
|
-
Reset links expire, and each one can be used only once. Ask for a fresh link and it will arrive in a moment.
|
|
82
|
-
</p>
|
|
83
|
-
<Button href="/forgot-password">
|
|
84
|
-
Send a new link
|
|
85
|
-
</Button>
|
|
76
|
+
<div :else-if="success()">
|
|
77
|
+
<div class="py-4 text-center">
|
|
78
|
+
<div class="inline-flex items-center justify-center mb-4 h-12 w-12 bg-green-100 dark:bg-green-900/30 rounded-full">
|
|
79
|
+
<div class="h-6 w-6 text-green-600 dark:text-green-400 i-hugeicons-checkmark-circle-02"></div>
|
|
86
80
|
</div>
|
|
81
|
+
<h2 class="mb-2 font-medium text-lg text-neutral-900 dark:text-white">
|
|
82
|
+
Password changed
|
|
83
|
+
</h2>
|
|
84
|
+
<p class="mb-6 text-neutral-500 text-sm dark:text-neutral-400">
|
|
85
|
+
You can sign in with your new password now.
|
|
86
|
+
</p>
|
|
87
|
+
<Button href="/login">
|
|
88
|
+
Go to sign in
|
|
89
|
+
</Button>
|
|
87
90
|
</div>
|
|
91
|
+
</div>
|
|
88
92
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
</
|
|
94
|
-
<h2 class="mb-2 font-medium text-lg text-neutral-900 dark:text-white">
|
|
95
|
-
Password changed
|
|
96
|
-
</h2>
|
|
97
|
-
<p class="mb-6 text-neutral-500 text-sm dark:text-neutral-400">
|
|
98
|
-
You can sign in with your new password now.
|
|
99
|
-
</p>
|
|
100
|
-
<Button href="/login">
|
|
101
|
-
Go to sign in
|
|
102
|
-
</Button>
|
|
93
|
+
<div :else>
|
|
94
|
+
<div :if="error()" role="alert" class="mb-4 p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 rounded-lg dark:border-red-800">
|
|
95
|
+
<div class="flex gap-2 items-center">
|
|
96
|
+
<div class="h-4 w-4 text-red-600 dark:text-red-400 i-hugeicons-alert-circle"></div>
|
|
97
|
+
<p class="text-red-600 text-sm dark:text-red-400">{{ error() }}</p>
|
|
103
98
|
</div>
|
|
104
99
|
</div>
|
|
105
100
|
|
|
106
|
-
<
|
|
107
|
-
<div
|
|
108
|
-
<
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
101
|
+
<form @submit.prevent="handleSubmit">
|
|
102
|
+
<div class="mb-4">
|
|
103
|
+
<Input
|
|
104
|
+
type="password"
|
|
105
|
+
label="New password"
|
|
106
|
+
placeholder="At least 8 characters"
|
|
107
|
+
autocomplete="new-password"
|
|
108
|
+
v-model:value="password"
|
|
109
|
+
required
|
|
110
|
+
:disabled="isLoading()"
|
|
111
|
+
/>
|
|
112
112
|
</div>
|
|
113
113
|
|
|
114
|
-
<
|
|
115
|
-
<
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
<div class="mb-6">
|
|
128
|
-
<Input
|
|
129
|
-
type="password"
|
|
130
|
-
label="Confirm new password"
|
|
131
|
-
placeholder="Repeat your new password"
|
|
132
|
-
autocomplete="new-password"
|
|
133
|
-
v-model:value="confirmation"
|
|
134
|
-
:error="mismatch() ? 'Both passwords must match' : ''"
|
|
135
|
-
required
|
|
136
|
-
:disabled="isLoading()"
|
|
137
|
-
/>
|
|
138
|
-
</div>
|
|
139
|
-
|
|
140
|
-
<Button
|
|
141
|
-
type="submit"
|
|
142
|
-
variant="primary"
|
|
143
|
-
size="lg"
|
|
144
|
-
fullWidth
|
|
145
|
-
:loading="isLoading()"
|
|
146
|
-
:disabled="!canSubmit()"
|
|
147
|
-
>
|
|
148
|
-
<span :if="isLoading()">Saving...</span>
|
|
149
|
-
<span :if="!isLoading()">Change password</span>
|
|
150
|
-
</Button>
|
|
151
|
-
</form>
|
|
152
|
-
</div>
|
|
153
|
-
</div>
|
|
114
|
+
<div class="mb-6">
|
|
115
|
+
<Input
|
|
116
|
+
type="password"
|
|
117
|
+
label="Confirm new password"
|
|
118
|
+
placeholder="Repeat your new password"
|
|
119
|
+
autocomplete="new-password"
|
|
120
|
+
v-model:value="confirmation"
|
|
121
|
+
:error="mismatch() ? 'Both passwords must match' : ''"
|
|
122
|
+
required
|
|
123
|
+
:disabled="isLoading()"
|
|
124
|
+
/>
|
|
125
|
+
</div>
|
|
154
126
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
127
|
+
<Button
|
|
128
|
+
type="submit"
|
|
129
|
+
variant="primary"
|
|
130
|
+
size="lg"
|
|
131
|
+
fullWidth
|
|
132
|
+
:loading="isLoading()"
|
|
133
|
+
:disabled="!canSubmit()"
|
|
134
|
+
>
|
|
135
|
+
<span :if="isLoading()">Saving...</span>
|
|
136
|
+
<span :if="!isLoading()">Change password</span>
|
|
137
|
+
</Button>
|
|
138
|
+
</form>
|
|
139
|
+
</div>
|
|
162
140
|
</div>
|
|
163
|
-
|
|
141
|
+
|
|
142
|
+
<!-- Back to Login -->
|
|
143
|
+
<p class="mt-6 text-center text-neutral-500 text-sm dark:text-neutral-400">
|
|
144
|
+
<a href="/login" class="inline-flex gap-1 items-center font-medium text-blue-600 dark:text-blue-400 hover:text-blue-500 dark:hover:text-blue-300">
|
|
145
|
+
<div class="h-4 w-4 i-hugeicons-arrow-left-01"></div>
|
|
146
|
+
Back to sign in
|
|
147
|
+
</a>
|
|
148
|
+
</p>
|
|
149
|
+
</AuthCard>
|
|
@@ -84,21 +84,17 @@ onMount(() => {
|
|
|
84
84
|
</script>
|
|
85
85
|
|
|
86
86
|
<div class="space-y-6">
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
Clear
|
|
99
|
-
</Button>
|
|
100
|
-
</div>
|
|
101
|
-
</header>
|
|
87
|
+
<PageHeader title="Buddy" description="Ask framework questions through your configured Stacks AI provider.">
|
|
88
|
+
<div class="flex gap-2 items-center">
|
|
89
|
+
<span :class="'inline-flex items-center px-2.5 py-1 font-medium text-xs rounded-full ' + (chatState()?.configured ? 'text-green-700 bg-green-100 dark:text-green-300 dark:bg-green-950' : 'text-amber-700 bg-amber-100 dark:text-amber-300 dark:bg-amber-950')">
|
|
90
|
+
<span :class="'mr-1.5 h-1.5 w-1.5 rounded-full ' + (chatState()?.configured ? 'bg-green-500' : 'bg-amber-500')"></span>
|
|
91
|
+
{{ providerName() }} {{ chatState()?.configured ? 'ready' : 'needs configuration' }}
|
|
92
|
+
</span>
|
|
93
|
+
<Button :if="messages().length > 0" variant="secondary" size="sm" @click="clearHistory">
|
|
94
|
+
Clear
|
|
95
|
+
</Button>
|
|
96
|
+
</div>
|
|
97
|
+
</PageHeader>
|
|
102
98
|
|
|
103
99
|
<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">
|
|
104
100
|
{{ loadError() }}
|
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
} from '../../../../app/Actions/Dashboard/Commerce/commerce-category-records'
|
|
6
6
|
import { DashboardApiError, dashboardApi } from '../../../../functions/dashboard-api'
|
|
7
7
|
import { pushToast } from '../../../../functions/toasts'
|
|
8
|
+
import { pageControls } from '../../../../functions/dashboard-list'
|
|
8
9
|
|
|
9
10
|
interface CategoryIndexResponse {
|
|
10
11
|
records: CommerceCategoryRecord[]
|
|
@@ -230,13 +231,7 @@ async function deleteCategory(): Promise<void> {
|
|
|
230
231
|
}
|
|
231
232
|
}
|
|
232
233
|
|
|
233
|
-
|
|
234
|
-
page.set(Math.max(1, page() - 1))
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function nextPage(): void {
|
|
238
|
-
page.set(Math.min(pageCount(), page() + 1))
|
|
239
|
-
}
|
|
234
|
+
const { previousPage, nextPage } = pageControls(page, pageCount)
|
|
240
235
|
|
|
241
236
|
onMount(() => {
|
|
242
237
|
void loadCategories()
|
|
@@ -244,29 +239,17 @@ onMount(() => {
|
|
|
244
239
|
</script>
|
|
245
240
|
|
|
246
241
|
<div class="space-y-6">
|
|
247
|
-
<
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
</
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
<Button tag="a" variant="secondary" :href="exportHref()" :download="'product-categories-' + new Date().toISOString().slice(0, 10) + '.csv'"><span class="h-4 w-4 i-hugeicons-download-04"></span>Export filtered</Button>
|
|
255
|
-
<Button @click="openAdd()"><span class="h-4 w-4 i-hugeicons-add-01"></span>Add category</Button>
|
|
256
|
-
</div>
|
|
257
|
-
</header>
|
|
242
|
+
<PageHeader title="Product Categories" description="Manage the native Category hierarchy, display order, and storefront availability.">
|
|
243
|
+
<div class="flex flex-wrap gap-2">
|
|
244
|
+
<Button variant="secondary" :loading="loading()" @click="loadCategories()"><span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>Refresh</Button>
|
|
245
|
+
<Button tag="a" variant="secondary" :href="exportHref()" :download="'product-categories-' + new Date().toISOString().slice(0, 10) + '.csv'"><span class="h-4 w-4 i-hugeicons-download-04"></span>Export filtered</Button>
|
|
246
|
+
<Button @click="openAdd()"><span class="h-4 w-4 i-hugeicons-add-01"></span>Add category</Button>
|
|
247
|
+
</div>
|
|
248
|
+
</PageHeader>
|
|
258
249
|
|
|
259
250
|
<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>
|
|
260
251
|
|
|
261
|
-
<
|
|
262
|
-
<template :for="item in metricCards()" :key="item.label">
|
|
263
|
-
<article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
264
|
-
<p class="text-gray-500 text-xs dark:text-neutral-400">{{ item.label }}</p>
|
|
265
|
-
<p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ item.value }}</p>
|
|
266
|
-
<p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">{{ item.detail }}</p>
|
|
267
|
-
</article>
|
|
268
|
-
</template>
|
|
269
|
-
</section>
|
|
252
|
+
<MetricCards label="Product category metrics" columns="grid-cols-2 lg:grid-cols-4" :items="metricCards()" />
|
|
270
253
|
|
|
271
254
|
<CommerceCategoryHierarchy :records="records()" />
|
|
272
255
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script client>
|
|
2
2
|
import type { CommerceCategoryRecord } from '../../../../app/Actions/Dashboard/Commerce/commerce-category-records'
|
|
3
|
+
import { formErrorMessages } from '../../../../functions/dashboard-form'
|
|
3
4
|
|
|
4
5
|
const open = useReactiveProp('open', false)
|
|
5
6
|
const mode = useReactiveProp('mode', 'add')
|
|
@@ -113,10 +114,7 @@ function submit(): void {
|
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
function errorMessages(): string[] {
|
|
116
|
-
return
|
|
117
|
-
const label = field.replace(/([a-z])([A-Z])/g, '$1 $2').replaceAll('_', ' ')
|
|
118
|
-
return `${label.charAt(0).toUpperCase()}${label.slice(1)}: ${message}`
|
|
119
|
-
})
|
|
117
|
+
return formErrorMessages(errors())
|
|
120
118
|
}
|
|
121
119
|
</script>
|
|
122
120
|
|
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
CouponStatus,
|
|
6
6
|
} from '../../../../app/Actions/Dashboard/Commerce/coupon-records'
|
|
7
7
|
import { minorToInput, moneyInputError, moneyInputStep, parseMoneyInput } from '@stacksjs/commerce/money'
|
|
8
|
+
import { formErrorMessages } from '../../../../functions/dashboard-form'
|
|
8
9
|
|
|
9
10
|
const open = useReactiveProp('open', false)
|
|
10
11
|
const mode = useReactiveProp('mode', 'add')
|
|
@@ -104,10 +105,7 @@ function submit(): void {
|
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
function errorMessages(): string[] {
|
|
107
|
-
return
|
|
108
|
-
const label = field.replace(/([a-z])([A-Z])/g, '$1 $2').replaceAll('_', ' ')
|
|
109
|
-
return `${label.charAt(0).toUpperCase()}${label.slice(1)}: ${message}`
|
|
110
|
-
})
|
|
108
|
+
return formErrorMessages(errors())
|
|
111
109
|
}
|
|
112
110
|
</script>
|
|
113
111
|
|
|
@@ -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 CouponIndexResponse {
|
|
13
14
|
records: CouponRecord[]
|
|
@@ -241,13 +242,7 @@ async function deleteCoupon(): Promise<void> {
|
|
|
241
242
|
}
|
|
242
243
|
}
|
|
243
244
|
|
|
244
|
-
|
|
245
|
-
page.set(Math.max(1, page() - 1))
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function nextPage(): void {
|
|
249
|
-
page.set(Math.min(pageCount(), page() + 1))
|
|
250
|
-
}
|
|
245
|
+
const { previousPage, nextPage } = pageControls(page, pageCount)
|
|
251
246
|
|
|
252
247
|
onMount(() => {
|
|
253
248
|
void loadCoupons()
|
|
@@ -255,29 +250,17 @@ onMount(() => {
|
|
|
255
250
|
</script>
|
|
256
251
|
|
|
257
252
|
<div class="space-y-6">
|
|
258
|
-
<
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
</
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
<Button tag="a" variant="secondary" :href="exportHref()" :download="'coupons-' + new Date().toISOString().slice(0, 10) + '.csv'"><span class="h-4 w-4 i-hugeicons-download-04"></span>Export filtered</Button>
|
|
266
|
-
<Button @click="openAdd()"><span class="h-4 w-4 i-hugeicons-add-01"></span>Add coupon</Button>
|
|
267
|
-
</div>
|
|
268
|
-
</header>
|
|
253
|
+
<PageHeader title="Coupons" description="Manage native Coupon records, availability, limits, and redemption state.">
|
|
254
|
+
<div class="flex flex-wrap gap-2">
|
|
255
|
+
<Button variant="secondary" :loading="loading()" @click="loadCoupons()"><span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>Refresh</Button>
|
|
256
|
+
<Button tag="a" variant="secondary" :href="exportHref()" :download="'coupons-' + new Date().toISOString().slice(0, 10) + '.csv'"><span class="h-4 w-4 i-hugeicons-download-04"></span>Export filtered</Button>
|
|
257
|
+
<Button @click="openAdd()"><span class="h-4 w-4 i-hugeicons-add-01"></span>Add coupon</Button>
|
|
258
|
+
</div>
|
|
259
|
+
</PageHeader>
|
|
269
260
|
|
|
270
261
|
<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>
|
|
271
262
|
|
|
272
|
-
<
|
|
273
|
-
<template :for="item in metricCards()" :key="item.label">
|
|
274
|
-
<article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
275
|
-
<p class="text-gray-500 text-xs dark:text-neutral-400">{{ item.label }}</p>
|
|
276
|
-
<p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ item.value }}</p>
|
|
277
|
-
<p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">{{ item.detail }}</p>
|
|
278
|
-
</article>
|
|
279
|
-
</template>
|
|
280
|
-
</section>
|
|
263
|
+
<MetricCards label="Coupon metrics" columns="grid-cols-2 lg:grid-cols-4" :items="metricCards()" />
|
|
281
264
|
|
|
282
265
|
<CommerceCouponBreakdown :records="records()" />
|
|
283
266
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script client>
|
|
2
2
|
import type { CommerceCustomerRecord } from '../../../../app/Actions/Dashboard/Commerce/commerce-customer-records'
|
|
3
3
|
import { minorToInput, moneyInputError, moneyInputStep, parseMoneyInput } from '@stacksjs/commerce/money'
|
|
4
|
+
import { formErrorMessages } from '../../../../functions/dashboard-form'
|
|
4
5
|
|
|
5
6
|
const open = useReactiveProp('open', false)
|
|
6
7
|
const mode = useReactiveProp('mode', 'add')
|
|
@@ -72,10 +73,7 @@ function submit(): void {
|
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
function errorMessages(): string[] {
|
|
75
|
-
return
|
|
76
|
-
const label = field.replace(/([a-z])([A-Z])/g, '$1 $2').replaceAll('_', ' ')
|
|
77
|
-
return `${label.charAt(0).toUpperCase()}${label.slice(1)}: ${message}`
|
|
78
|
-
})
|
|
76
|
+
return formErrorMessages(errors())
|
|
79
77
|
}
|
|
80
78
|
</script>
|
|
81
79
|
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
import { DashboardApiError, dashboardApi } from '../../../../functions/dashboard-api'
|
|
7
7
|
import { pushToast } from '../../../../functions/toasts'
|
|
8
8
|
import { formatCurrency, minorToInput } from '@stacksjs/commerce/money'
|
|
9
|
+
import { pageControls } from '../../../../functions/dashboard-list'
|
|
9
10
|
|
|
10
11
|
interface CustomerIndexResponse {
|
|
11
12
|
records: CommerceCustomerRecord[]
|
|
@@ -243,13 +244,7 @@ async function deleteCustomer(): Promise<void> {
|
|
|
243
244
|
}
|
|
244
245
|
}
|
|
245
246
|
|
|
246
|
-
|
|
247
|
-
page.set(Math.max(1, page() - 1))
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function nextPage(): void {
|
|
251
|
-
page.set(Math.min(pageCount(), page() + 1))
|
|
252
|
-
}
|
|
247
|
+
const { previousPage, nextPage } = pageControls(page, pageCount)
|
|
253
248
|
|
|
254
249
|
onMount(() => {
|
|
255
250
|
void loadCustomers()
|
|
@@ -257,29 +252,17 @@ onMount(() => {
|
|
|
257
252
|
</script>
|
|
258
253
|
|
|
259
254
|
<div class="space-y-6">
|
|
260
|
-
<
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
</
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
<Button tag="a" variant="secondary" :href="exportHref()" :download="'customers-' + new Date().toISOString().slice(0, 10) + '.csv'"><span class="h-4 w-4 i-hugeicons-download-04"></span>Export filtered</Button>
|
|
268
|
-
<Button @click="openAdd()"><span class="h-4 w-4 i-hugeicons-add-01"></span>Add customer</Button>
|
|
269
|
-
</div>
|
|
270
|
-
</header>
|
|
255
|
+
<PageHeader title="Customers" description="Manage native Customer records, contact details, status, and recorded spend.">
|
|
256
|
+
<div class="flex flex-wrap gap-2">
|
|
257
|
+
<Button variant="secondary" :loading="loading()" @click="loadCustomers()"><span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>Refresh</Button>
|
|
258
|
+
<Button tag="a" variant="secondary" :href="exportHref()" :download="'customers-' + new Date().toISOString().slice(0, 10) + '.csv'"><span class="h-4 w-4 i-hugeicons-download-04"></span>Export filtered</Button>
|
|
259
|
+
<Button @click="openAdd()"><span class="h-4 w-4 i-hugeicons-add-01"></span>Add customer</Button>
|
|
260
|
+
</div>
|
|
261
|
+
</PageHeader>
|
|
271
262
|
|
|
272
263
|
<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>
|
|
273
264
|
|
|
274
|
-
<
|
|
275
|
-
<template :for="item in metricCards()" :key="item.label">
|
|
276
|
-
<article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
277
|
-
<p class="text-gray-500 text-xs dark:text-neutral-400">{{ item.label }}</p>
|
|
278
|
-
<p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ item.value }}</p>
|
|
279
|
-
<p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">{{ item.detail }}</p>
|
|
280
|
-
</article>
|
|
281
|
-
</template>
|
|
282
|
-
</section>
|
|
265
|
+
<MetricCards label="Customer metrics" columns="grid-cols-2 lg:grid-cols-4" :items="metricCards()" />
|
|
283
266
|
|
|
284
267
|
<section class="space-y-3">
|
|
285
268
|
<div class="grid gap-3 grid-cols-1 sm:grid-cols-2 lg:grid-cols-4">
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script client>
|
|
2
2
|
import type { GiftCardRecord, GiftCardStatus } from '../../../../app/Actions/Dashboard/Commerce/gift-card-records'
|
|
3
3
|
import { minorToInput, moneyInputError, moneyInputStep, parseMoneyInput } from '@stacksjs/commerce/money'
|
|
4
|
+
import { formErrorMessages } from '../../../../functions/dashboard-form'
|
|
4
5
|
|
|
5
6
|
const open = useReactiveProp('open', false)
|
|
6
7
|
const mode = useReactiveProp('mode', 'add')
|
|
@@ -148,10 +149,7 @@ function submit(): void {
|
|
|
148
149
|
}
|
|
149
150
|
|
|
150
151
|
function errorMessages(): string[] {
|
|
151
|
-
return
|
|
152
|
-
const label = field.replace(/([a-z])([A-Z])/g, '$1 $2').replaceAll('_', ' ')
|
|
153
|
-
return `${label.charAt(0).toUpperCase()}${label.slice(1)}: ${message}`
|
|
154
|
-
})
|
|
152
|
+
return formErrorMessages(errors())
|
|
155
153
|
}
|
|
156
154
|
</script>
|
|
157
155
|
|
|
@@ -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 { minorToInput } from '@stacksjs/commerce/money'
|
|
10
|
+
import { pageControls } from '../../../../functions/dashboard-list'
|
|
10
11
|
|
|
11
12
|
interface GiftCardIndexResponse {
|
|
12
13
|
records: GiftCardRecord[]
|
|
@@ -257,13 +258,7 @@ async function deleteGiftCard(): Promise<void> {
|
|
|
257
258
|
}
|
|
258
259
|
}
|
|
259
260
|
|
|
260
|
-
|
|
261
|
-
page.set(Math.max(1, page() - 1))
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function nextPage(): void {
|
|
265
|
-
page.set(Math.min(pageCount(), page() + 1))
|
|
266
|
-
}
|
|
261
|
+
const { previousPage, nextPage } = pageControls(page, pageCount)
|
|
267
262
|
|
|
268
263
|
onMount(() => {
|
|
269
264
|
void loadGiftCards()
|
|
@@ -271,29 +266,17 @@ onMount(() => {
|
|
|
271
266
|
</script>
|
|
272
267
|
|
|
273
268
|
<div class="space-y-6">
|
|
274
|
-
<
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
</
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
<Button tag="a" variant="secondary" :href="exportHref()" :download="'gift-cards-' + new Date().toISOString().slice(0, 10) + '.csv'"><span class="h-4 w-4 i-hugeicons-download-04"></span>Export filtered</Button>
|
|
282
|
-
<Button @click="openAdd()"><span class="h-4 w-4 i-hugeicons-add-01"></span>Add gift card</Button>
|
|
283
|
-
</div>
|
|
284
|
-
</header>
|
|
269
|
+
<PageHeader title="Gift Cards" description="Manage native GiftCard balances, recipients, delivery, and lifecycle state.">
|
|
270
|
+
<div class="flex flex-wrap gap-2">
|
|
271
|
+
<Button variant="secondary" :loading="loading()" @click="loadGiftCards()"><span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>Refresh</Button>
|
|
272
|
+
<Button tag="a" variant="secondary" :href="exportHref()" :download="'gift-cards-' + new Date().toISOString().slice(0, 10) + '.csv'"><span class="h-4 w-4 i-hugeicons-download-04"></span>Export filtered</Button>
|
|
273
|
+
<Button @click="openAdd()"><span class="h-4 w-4 i-hugeicons-add-01"></span>Add gift card</Button>
|
|
274
|
+
</div>
|
|
275
|
+
</PageHeader>
|
|
285
276
|
|
|
286
277
|
<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>
|
|
287
278
|
|
|
288
|
-
<
|
|
289
|
-
<template :for="item in metricCards()" :key="item.label">
|
|
290
|
-
<article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
291
|
-
<p class="text-gray-500 text-xs dark:text-neutral-400">{{ item.label }}</p>
|
|
292
|
-
<p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ item.value }}</p>
|
|
293
|
-
<p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">{{ item.detail }}</p>
|
|
294
|
-
</article>
|
|
295
|
-
</template>
|
|
296
|
-
</section>
|
|
279
|
+
<MetricCards label="Gift card metrics" columns="grid-cols-2 lg:grid-cols-4" :items="metricCards()" />
|
|
297
280
|
|
|
298
281
|
<CommerceGiftCardBreakdown :summary="summary()" />
|
|
299
282
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script client>
|
|
2
2
|
import type { ManufacturerRecord } from '../../../../app/Actions/Dashboard/Commerce/manufacturer-records'
|
|
3
|
+
import { formErrorMessages } from '../../../../functions/dashboard-form'
|
|
3
4
|
|
|
4
5
|
const open = useReactiveProp('open', false)
|
|
5
6
|
const mode = useReactiveProp('mode', 'add')
|
|
@@ -41,10 +42,7 @@ function submit(): void {
|
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
function errorMessages(): string[] {
|
|
44
|
-
return
|
|
45
|
-
const label = field.replace(/([a-z])([A-Z])/g, '$1 $2').replaceAll('_', ' ')
|
|
46
|
-
return `${label.charAt(0).toUpperCase()}${label.slice(1)}: ${message}`
|
|
47
|
-
})
|
|
45
|
+
return formErrorMessages(errors())
|
|
48
46
|
}
|
|
49
47
|
</script>
|
|
50
48
|
|