@stacksjs/defaults 0.75.230 → 0.75.233
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 +15 -0
- package/ai/skills/stacks-models/SKILL.md +6 -1
- package/ai/skills/stacks-native/CATALOG.md +6 -6
- package/ai/skills/stacks-storage/SKILL.md +43 -0
- package/ai/skills/stacks-testing/SKILL.md +38 -10
- package/app/Actions/Dashboard/Models/GetModels.ts +19 -14
- package/app/Actions/Dashboard/Models/GetSubscriberCount.ts +5 -2
- package/app/Actions/Dashboard/Models/GetUserCount.ts +5 -2
- package/app/Actions/Dashboard/Models/ModelShowAction.ts +3 -11
- package/app/Actions/Dashboard/Models/ModelsIndexAction.ts +6 -3
- package/app/Actions/Dashboard/Models/model-write.ts +2 -3
- package/app/Actions/Dashboard/Search/GlobalSearchAction.ts +6 -2
- 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/Commerce/CommerceCategoriesDashboard.stx +2 -7
- 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 +2 -7
- package/resources/components/Dashboard/Commerce/CommerceCustomerDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceCustomersDashboard.stx +2 -7
- package/resources/components/Dashboard/Commerce/CommerceGiftCardDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceGiftCardsDashboard.stx +2 -7
- package/resources/components/Dashboard/Commerce/CommerceManufacturerDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceOrderDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceOrdersDashboard.stx +2 -7
- package/resources/components/Dashboard/Commerce/CommerceProductDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceProductsDashboard.stx +2 -7
- package/resources/components/Dashboard/Commerce/CommerceReviewEditDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceTaxDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceUnitDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/CommerceVariantDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/Delivery/CouriersDashboard.stx +45 -92
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryRoutesDashboard.stx +46 -92
- package/resources/components/Dashboard/Commerce/Delivery/LicenseKeysDashboard.stx +46 -92
- package/resources/components/Dashboard/Commerce/Delivery/ShippingRatesDashboard.stx +44 -90
- package/resources/components/Dashboard/Commerce/Delivery/ShippingZonesDashboard.stx +45 -91
- package/resources/components/Dashboard/Commerce/PrintDeviceDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/PrintDevicesDashboard.stx +2 -7
- package/resources/components/Dashboard/Commerce/ProductWaitlistEntryDialog.stx +2 -4
- package/resources/components/Dashboard/Commerce/RestaurantWaitlistEntryDialog.stx +2 -4
- package/resources/components/Dashboard/Marketing/AbandonedCartsDashboard.stx +2 -9
- package/resources/components/Dashboard/Marketing/CampaignsDashboard.stx +2 -9
- package/resources/components/Dashboard/Marketing/MarketingListsDashboard.stx +2 -9
- package/resources/components/Dashboard/Marketing/SocialPostsDashboard.stx +2 -9
- package/resources/functions/dashboard/model-access.ts +15 -0
- package/resources/plugins/preloader.ts +9 -1
|
@@ -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()
|
|
@@ -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()
|
|
@@ -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
|
|
|
@@ -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()
|
|
@@ -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()
|
|
@@ -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
|
|
|
@@ -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
|
|
|
@@ -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
|
|
|
@@ -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
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script client>
|
|
2
2
|
import { dashboardApi } from '../../../../../functions/dashboard-api'
|
|
3
|
+
import { useRecordsDashboard } from '../../../../../functions/dashboard-list'
|
|
3
4
|
import { pushToast } from '../../../../../functions/toasts'
|
|
4
5
|
|
|
5
6
|
type CourierStatus = 'active' | 'on_delivery' | 'on_break'
|
|
@@ -34,21 +35,48 @@ interface CourierPayload {
|
|
|
34
35
|
userId: number | null
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
function matchesQuery(records: CourierRecord[], query: string): CourierRecord[] {
|
|
39
|
+
return records.filter(record => [
|
|
40
|
+
record.name,
|
|
41
|
+
record.phone,
|
|
42
|
+
record.vehicle_number,
|
|
43
|
+
record.license,
|
|
44
|
+
record.status,
|
|
45
|
+
record.user?.name,
|
|
46
|
+
record.user?.email,
|
|
47
|
+
].some(value => String(value || '').toLowerCase().includes(query)))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const {
|
|
51
|
+
records,
|
|
52
|
+
loading,
|
|
53
|
+
loadError,
|
|
54
|
+
search,
|
|
55
|
+
page,
|
|
56
|
+
perPage,
|
|
57
|
+
editorOpen,
|
|
58
|
+
editorMode,
|
|
59
|
+
editing,
|
|
60
|
+
saving,
|
|
61
|
+
editorError,
|
|
62
|
+
deleting,
|
|
63
|
+
deleteBusy,
|
|
64
|
+
deleteError,
|
|
65
|
+
filteredRecords,
|
|
66
|
+
paginatedRecords,
|
|
67
|
+
goToPage,
|
|
68
|
+
handleSearch,
|
|
69
|
+
openCreate,
|
|
70
|
+
openEdit,
|
|
71
|
+
closeEditor,
|
|
72
|
+
requestDelete,
|
|
73
|
+
closeDelete,
|
|
74
|
+
upsert,
|
|
75
|
+
remove,
|
|
76
|
+
clampPage,
|
|
77
|
+
} = useRecordsDashboard<CourierRecord>({ filter: matchesQuery })
|
|
78
|
+
|
|
38
79
|
const users = state<UserOption[]>([])
|
|
39
|
-
const loading = state(true)
|
|
40
|
-
const loadError = state('')
|
|
41
|
-
const search = state('')
|
|
42
|
-
const page = state(1)
|
|
43
|
-
const perPage = state(10)
|
|
44
|
-
const editorOpen = state(false)
|
|
45
|
-
const editorMode = state<'create' | 'edit'>('create')
|
|
46
|
-
const editing = state<CourierRecord | null>(null)
|
|
47
|
-
const saving = state(false)
|
|
48
|
-
const editorError = state('')
|
|
49
|
-
const deleting = state<CourierRecord | null>(null)
|
|
50
|
-
const deleteBusy = state(false)
|
|
51
|
-
const deleteError = state('')
|
|
52
80
|
|
|
53
81
|
function userFor(id?: number | null): UserOption | undefined {
|
|
54
82
|
return id ? users().find(user => user.id === id) : undefined
|
|
@@ -61,26 +89,6 @@ function withUser(record: CourierRecord): CourierRecord {
|
|
|
61
89
|
}
|
|
62
90
|
}
|
|
63
91
|
|
|
64
|
-
function filteredRecords(): CourierRecord[] {
|
|
65
|
-
const query = search().trim().toLowerCase()
|
|
66
|
-
if (!query)
|
|
67
|
-
return records()
|
|
68
|
-
return records().filter(record => [
|
|
69
|
-
record.name,
|
|
70
|
-
record.phone,
|
|
71
|
-
record.vehicle_number,
|
|
72
|
-
record.license,
|
|
73
|
-
record.status,
|
|
74
|
-
record.user?.name,
|
|
75
|
-
record.user?.email,
|
|
76
|
-
].some(value => String(value || '').toLowerCase().includes(query)))
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function paginatedRecords(): CourierRecord[] {
|
|
80
|
-
const start = (page() - 1) * perPage()
|
|
81
|
-
return filteredRecords().slice(start, start + perPage())
|
|
82
|
-
}
|
|
83
|
-
|
|
84
92
|
function statusCount(status: CourierStatus): number {
|
|
85
93
|
return records().filter(record => record.status === status).length
|
|
86
94
|
}
|
|
@@ -106,9 +114,7 @@ async function loadCouriers(): Promise<void> {
|
|
|
106
114
|
throw new TypeError('Courier resources were not returned in the expected shape.')
|
|
107
115
|
users.set(userData.users)
|
|
108
116
|
records.set(courierData.map(withUser))
|
|
109
|
-
|
|
110
|
-
if (page() > lastPage)
|
|
111
|
-
page.set(lastPage)
|
|
117
|
+
clampPage()
|
|
112
118
|
}
|
|
113
119
|
catch (error) {
|
|
114
120
|
loadError.set(error instanceof Error ? error.message : String(error))
|
|
@@ -119,33 +125,6 @@ async function loadCouriers(): Promise<void> {
|
|
|
119
125
|
}
|
|
120
126
|
}
|
|
121
127
|
|
|
122
|
-
function handleSearch(value: string): void {
|
|
123
|
-
search.set(value)
|
|
124
|
-
page.set(1)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function openCreate(): void {
|
|
128
|
-
editing.set(null)
|
|
129
|
-
editorMode.set('create')
|
|
130
|
-
editorError.set('')
|
|
131
|
-
editorOpen.set(true)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function openEdit(record: CourierRecord): void {
|
|
135
|
-
editing.set(record)
|
|
136
|
-
editorMode.set('edit')
|
|
137
|
-
editorError.set('')
|
|
138
|
-
editorOpen.set(true)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function closeEditor(): void {
|
|
142
|
-
if (saving())
|
|
143
|
-
return
|
|
144
|
-
editorOpen.set(false)
|
|
145
|
-
editing.set(null)
|
|
146
|
-
editorError.set('')
|
|
147
|
-
}
|
|
148
|
-
|
|
149
128
|
function setEditorError(message: string): void {
|
|
150
129
|
editorError.set(message)
|
|
151
130
|
}
|
|
@@ -165,14 +144,7 @@ async function saveCourier(payload: CourierPayload): Promise<void> {
|
|
|
165
144
|
},
|
|
166
145
|
)
|
|
167
146
|
const hydrated = withUser(updated)
|
|
168
|
-
|
|
169
|
-
records.set(records().map(record => record.id === current.id ? hydrated : record))
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
const nextRecords = [...records(), hydrated]
|
|
173
|
-
records.set(nextRecords)
|
|
174
|
-
page.set(Math.max(1, Math.ceil(nextRecords.length / perPage())))
|
|
175
|
-
}
|
|
147
|
+
upsert(hydrated, current)
|
|
176
148
|
editorOpen.set(false)
|
|
177
149
|
editing.set(null)
|
|
178
150
|
pushToast('success', current ? 'Courier updated' : 'Courier added', { detail: hydrated.name })
|
|
@@ -185,18 +157,6 @@ async function saveCourier(payload: CourierPayload): Promise<void> {
|
|
|
185
157
|
}
|
|
186
158
|
}
|
|
187
159
|
|
|
188
|
-
function requestDelete(record: CourierRecord): void {
|
|
189
|
-
deleting.set(record)
|
|
190
|
-
deleteError.set('')
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function closeDelete(): void {
|
|
194
|
-
if (deleteBusy())
|
|
195
|
-
return
|
|
196
|
-
deleting.set(null)
|
|
197
|
-
deleteError.set('')
|
|
198
|
-
}
|
|
199
|
-
|
|
200
160
|
async function confirmDelete(): Promise<void> {
|
|
201
161
|
const current = deleting()
|
|
202
162
|
if (!current)
|
|
@@ -207,11 +167,8 @@ async function confirmDelete(): Promise<void> {
|
|
|
207
167
|
await dashboardApi(`/api/dashboard/commerce/couriers/${current.id}`, {
|
|
208
168
|
method: 'DELETE',
|
|
209
169
|
})
|
|
210
|
-
|
|
170
|
+
remove(current)
|
|
211
171
|
deleting.set(null)
|
|
212
|
-
const lastPage = Math.max(1, Math.ceil(filteredRecords().length / perPage()))
|
|
213
|
-
if (page() > lastPage)
|
|
214
|
-
page.set(lastPage)
|
|
215
172
|
pushToast('success', 'Courier deleted', { detail: current.name })
|
|
216
173
|
}
|
|
217
174
|
catch (error) {
|
|
@@ -222,10 +179,6 @@ async function confirmDelete(): Promise<void> {
|
|
|
222
179
|
}
|
|
223
180
|
}
|
|
224
181
|
|
|
225
|
-
function goToPage(value: number): void {
|
|
226
|
-
page.set(value)
|
|
227
|
-
}
|
|
228
|
-
|
|
229
182
|
onMount(() => {
|
|
230
183
|
void loadCouriers()
|
|
231
184
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script client>
|
|
2
2
|
import { dashboardApi } from '../../../../../functions/dashboard-api'
|
|
3
|
+
import { useRecordsDashboard } from '../../../../../functions/dashboard-list'
|
|
3
4
|
import { pushToast } from '../../../../../functions/toasts'
|
|
4
5
|
|
|
5
6
|
interface CourierRecord {
|
|
@@ -31,21 +32,48 @@ interface DeliveryRoutePayload {
|
|
|
31
32
|
lastActive: number
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
function matchesQuery(records: DeliveryRouteRecord[], query: string): DeliveryRouteRecord[] {
|
|
36
|
+
return records.filter(record => [
|
|
37
|
+
record.id,
|
|
38
|
+
record.courier,
|
|
39
|
+
record.courier_record?.name,
|
|
40
|
+
record.vehicle,
|
|
41
|
+
record.stops,
|
|
42
|
+
record.delivery_time,
|
|
43
|
+
record.total_distance,
|
|
44
|
+
].some(value => String(value || '').toLowerCase().includes(query)))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const {
|
|
48
|
+
records,
|
|
49
|
+
loading,
|
|
50
|
+
loadError,
|
|
51
|
+
search,
|
|
52
|
+
page,
|
|
53
|
+
perPage,
|
|
54
|
+
editorOpen,
|
|
55
|
+
editorMode,
|
|
56
|
+
editing,
|
|
57
|
+
saving,
|
|
58
|
+
editorError,
|
|
59
|
+
deleting,
|
|
60
|
+
deleteBusy,
|
|
61
|
+
deleteError,
|
|
62
|
+
filteredRecords,
|
|
63
|
+
paginatedRecords,
|
|
64
|
+
goToPage,
|
|
65
|
+
handleSearch,
|
|
66
|
+
openCreate,
|
|
67
|
+
openEdit,
|
|
68
|
+
closeEditor,
|
|
69
|
+
requestDelete,
|
|
70
|
+
closeDelete,
|
|
71
|
+
upsert,
|
|
72
|
+
remove,
|
|
73
|
+
clampPage,
|
|
74
|
+
} = useRecordsDashboard<DeliveryRouteRecord>({ filter: matchesQuery })
|
|
75
|
+
|
|
35
76
|
const couriers = state<CourierRecord[]>([])
|
|
36
|
-
const loading = state(true)
|
|
37
|
-
const loadError = state('')
|
|
38
|
-
const search = state('')
|
|
39
|
-
const page = state(1)
|
|
40
|
-
const perPage = state(10)
|
|
41
|
-
const editorOpen = state(false)
|
|
42
|
-
const editorMode = state<'create' | 'edit'>('create')
|
|
43
|
-
const editing = state<DeliveryRouteRecord | null>(null)
|
|
44
|
-
const saving = state(false)
|
|
45
|
-
const editorError = state('')
|
|
46
|
-
const deleting = state<DeliveryRouteRecord | null>(null)
|
|
47
|
-
const deleteBusy = state(false)
|
|
48
|
-
const deleteError = state('')
|
|
49
77
|
|
|
50
78
|
function courierFor(id?: number | null): CourierRecord | undefined {
|
|
51
79
|
return id ? couriers().find(courier => courier.id === id) : undefined
|
|
@@ -64,26 +92,6 @@ function routeTime(value: string | number): number {
|
|
|
64
92
|
return new Date(String(value).replace(' ', 'T')).getTime()
|
|
65
93
|
}
|
|
66
94
|
|
|
67
|
-
function filteredRecords(): DeliveryRouteRecord[] {
|
|
68
|
-
const query = search().trim().toLowerCase()
|
|
69
|
-
if (!query)
|
|
70
|
-
return records()
|
|
71
|
-
return records().filter(record => [
|
|
72
|
-
record.id,
|
|
73
|
-
record.courier,
|
|
74
|
-
record.courier_record?.name,
|
|
75
|
-
record.vehicle,
|
|
76
|
-
record.stops,
|
|
77
|
-
record.delivery_time,
|
|
78
|
-
record.total_distance,
|
|
79
|
-
].some(value => String(value || '').toLowerCase().includes(query)))
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function paginatedRecords(): DeliveryRouteRecord[] {
|
|
83
|
-
const start = (page() - 1) * perPage()
|
|
84
|
-
return filteredRecords().slice(start, start + perPage())
|
|
85
|
-
}
|
|
86
|
-
|
|
87
95
|
function metricCards(): Array<{ label: string, value: string, detail: string }> {
|
|
88
96
|
const totalStops = records().reduce((sum, record) => sum + record.stops, 0)
|
|
89
97
|
const totalDistance = records().reduce((sum, record) => sum + record.total_distance, 0)
|
|
@@ -109,9 +117,7 @@ async function loadRoutes(): Promise<void> {
|
|
|
109
117
|
throw new TypeError('Delivery route resources were not returned in the expected shape.')
|
|
110
118
|
couriers.set(courierData)
|
|
111
119
|
records.set(routeData.map(withCourier))
|
|
112
|
-
|
|
113
|
-
if (page() > lastPage)
|
|
114
|
-
page.set(lastPage)
|
|
120
|
+
clampPage()
|
|
115
121
|
}
|
|
116
122
|
catch (error) {
|
|
117
123
|
loadError.set(error instanceof Error ? error.message : String(error))
|
|
@@ -122,33 +128,6 @@ async function loadRoutes(): Promise<void> {
|
|
|
122
128
|
}
|
|
123
129
|
}
|
|
124
130
|
|
|
125
|
-
function handleSearch(value: string): void {
|
|
126
|
-
search.set(value)
|
|
127
|
-
page.set(1)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function openCreate(): void {
|
|
131
|
-
editing.set(null)
|
|
132
|
-
editorMode.set('create')
|
|
133
|
-
editorError.set('')
|
|
134
|
-
editorOpen.set(true)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function openEdit(record: DeliveryRouteRecord): void {
|
|
138
|
-
editing.set(record)
|
|
139
|
-
editorMode.set('edit')
|
|
140
|
-
editorError.set('')
|
|
141
|
-
editorOpen.set(true)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function closeEditor(): void {
|
|
145
|
-
if (saving())
|
|
146
|
-
return
|
|
147
|
-
editorOpen.set(false)
|
|
148
|
-
editing.set(null)
|
|
149
|
-
editorError.set('')
|
|
150
|
-
}
|
|
151
|
-
|
|
152
131
|
function setEditorError(message: string): void {
|
|
153
132
|
editorError.set(message)
|
|
154
133
|
}
|
|
@@ -168,14 +147,7 @@ async function saveRoute(payload: DeliveryRoutePayload): Promise<void> {
|
|
|
168
147
|
},
|
|
169
148
|
)
|
|
170
149
|
const hydrated = withCourier(updated)
|
|
171
|
-
|
|
172
|
-
records.set(records().map(record => record.id === current.id ? hydrated : record))
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
const nextRecords = [...records(), hydrated]
|
|
176
|
-
records.set(nextRecords)
|
|
177
|
-
page.set(Math.max(1, Math.ceil(nextRecords.length / perPage())))
|
|
178
|
-
}
|
|
150
|
+
upsert(hydrated, current)
|
|
179
151
|
editorOpen.set(false)
|
|
180
152
|
editing.set(null)
|
|
181
153
|
pushToast('success', current ? 'Delivery route updated' : 'Delivery route added', { detail: `Route #${hydrated.id}` })
|
|
@@ -188,18 +160,6 @@ async function saveRoute(payload: DeliveryRoutePayload): Promise<void> {
|
|
|
188
160
|
}
|
|
189
161
|
}
|
|
190
162
|
|
|
191
|
-
function requestDelete(record: DeliveryRouteRecord): void {
|
|
192
|
-
deleting.set(record)
|
|
193
|
-
deleteError.set('')
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function closeDelete(): void {
|
|
197
|
-
if (deleteBusy())
|
|
198
|
-
return
|
|
199
|
-
deleting.set(null)
|
|
200
|
-
deleteError.set('')
|
|
201
|
-
}
|
|
202
|
-
|
|
203
163
|
async function confirmDelete(): Promise<void> {
|
|
204
164
|
const current = deleting()
|
|
205
165
|
if (!current)
|
|
@@ -210,11 +170,9 @@ async function confirmDelete(): Promise<void> {
|
|
|
210
170
|
await dashboardApi(`/api/dashboard/commerce/delivery-routes/${current.id}`, {
|
|
211
171
|
method: 'DELETE',
|
|
212
172
|
})
|
|
213
|
-
|
|
173
|
+
remove(current)
|
|
214
174
|
deleting.set(null)
|
|
215
|
-
|
|
216
|
-
if (page() > lastPage)
|
|
217
|
-
page.set(lastPage)
|
|
175
|
+
clampPage()
|
|
218
176
|
pushToast('success', 'Delivery route deleted', { detail: `Route #${current.id}` })
|
|
219
177
|
}
|
|
220
178
|
catch (error) {
|
|
@@ -225,10 +183,6 @@ async function confirmDelete(): Promise<void> {
|
|
|
225
183
|
}
|
|
226
184
|
}
|
|
227
185
|
|
|
228
|
-
function goToPage(value: number): void {
|
|
229
|
-
page.set(value)
|
|
230
|
-
}
|
|
231
|
-
|
|
232
186
|
onMount(() => {
|
|
233
187
|
void loadRoutes()
|
|
234
188
|
})
|