@stacksjs/defaults 0.72.103 → 0.73.0
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/AGENTS.md +25 -4
- package/ai/README.md +26 -4
- package/ai/skills/stacks-actions/SKILL.md +1 -1
- package/ai/skills/stacks-ai/SKILL.md +1 -1
- package/ai/skills/stacks-alias/SKILL.md +1 -1
- package/ai/skills/stacks-analytics/SKILL.md +1 -1
- package/ai/skills/stacks-api/SKILL.md +1 -1
- package/ai/skills/stacks-arrays/SKILL.md +1 -1
- package/ai/skills/stacks-auto-imports/SKILL.md +1 -1
- package/ai/skills/stacks-browse/SKILL.md +1 -1
- package/ai/skills/stacks-browser/SKILL.md +1 -1
- package/ai/skills/stacks-buddy/SKILL.md +1 -1
- package/ai/skills/stacks-build/SKILL.md +79 -5
- package/ai/skills/stacks-cache/SKILL.md +1 -1
- package/ai/skills/stacks-calendar/SKILL.md +1 -1
- package/ai/skills/stacks-chat/SKILL.md +1 -1
- package/ai/skills/stacks-cli/SKILL.md +1 -1
- package/ai/skills/stacks-cloud/SKILL.md +1 -1
- package/ai/skills/stacks-cms/SKILL.md +1 -1
- package/ai/skills/stacks-codebase-design/DEEPENING.md +79 -0
- package/ai/skills/stacks-codebase-design/DESIGN-IT-TWICE.md +72 -0
- package/ai/skills/stacks-codebase-design/SKILL.md +180 -0
- package/ai/skills/stacks-collections/SKILL.md +1 -1
- package/ai/skills/stacks-commerce/SKILL.md +141 -35
- package/ai/skills/stacks-composables/SKILL.md +1 -1
- package/ai/skills/stacks-config/SKILL.md +1 -1
- package/ai/skills/stacks-configuration/SKILL.md +1 -1
- package/ai/skills/stacks-cron/SKILL.md +1 -1
- package/ai/skills/stacks-crosswind/SKILL.md +1 -1
- package/ai/skills/stacks-database/SKILL.md +1 -1
- package/ai/skills/stacks-datetime/SKILL.md +1 -1
- package/ai/skills/stacks-dependencies/SKILL.md +1 -1
- package/ai/skills/stacks-deploy/SKILL.md +1 -1
- package/ai/skills/stacks-desktop/SKILL.md +1 -1
- package/ai/skills/stacks-development/SKILL.md +1 -1
- package/ai/skills/stacks-dns/SKILL.md +1 -1
- package/ai/skills/stacks-docs/SKILL.md +1 -1
- package/ai/skills/stacks-domain-modeling/FORMATS.md +123 -0
- package/ai/skills/stacks-domain-modeling/SKILL.md +109 -0
- package/ai/skills/stacks-enums/SKILL.md +1 -1
- package/ai/skills/stacks-error-handling/SKILL.md +1 -1
- package/ai/skills/stacks-events/SKILL.md +1 -1
- package/ai/skills/stacks-faker/SKILL.md +1 -1
- package/ai/skills/stacks-flow/PHASE-BOUNDARIES.md +91 -0
- package/ai/skills/stacks-flow/SKILL.md +117 -0
- package/ai/skills/stacks-git/SKILL.md +37 -9
- package/ai/skills/stacks-grilling/SKILL.md +85 -0
- package/ai/skills/stacks-guard/SKILL.md +86 -11
- package/ai/skills/stacks-guard/scripts/block-destructive.sh +67 -0
- package/ai/skills/stacks-handoff/SKILL.md +70 -0
- package/ai/skills/stacks-health/SKILL.md +1 -1
- package/ai/skills/stacks-http/SKILL.md +1 -1
- package/ai/skills/stacks-i18n/SKILL.md +1 -1
- package/ai/skills/stacks-investigate/SKILL.md +234 -106
- package/ai/skills/stacks-investigate/scripts/hitl-loop.template.sh +47 -0
- package/ai/skills/stacks-jobs/SKILL.md +1 -1
- package/ai/skills/stacks-listeners/SKILL.md +1 -1
- package/ai/skills/stacks-logging/SKILL.md +1 -1
- package/ai/skills/stacks-mail/SKILL.md +1 -1
- package/ai/skills/stacks-middleware/SKILL.md +1 -1
- package/ai/skills/stacks-migrations/SKILL.md +1 -1
- package/ai/skills/stacks-models/SKILL.md +1 -1
- package/ai/skills/stacks-new-feature/SKILL.md +65 -5
- package/ai/skills/stacks-notifications/SKILL.md +1 -1
- package/ai/skills/stacks-objects/SKILL.md +1 -1
- package/ai/skills/stacks-office-hours/SKILL.md +16 -2
- package/ai/skills/stacks-orm/SKILL.md +1 -1
- package/ai/skills/stacks-path/SKILL.md +1 -1
- package/ai/skills/stacks-payments/SKILL.md +35 -1
- package/ai/skills/stacks-plan-review/SKILL.md +27 -6
- package/ai/skills/stacks-plugins/SKILL.md +1 -1
- package/ai/skills/stacks-prototype/LOGIC.md +103 -0
- package/ai/skills/stacks-prototype/SKILL.md +66 -0
- package/ai/skills/stacks-prototype/UI.md +112 -0
- package/ai/skills/stacks-push/SKILL.md +1 -1
- package/ai/skills/stacks-query-builder/SKILL.md +1 -1
- package/ai/skills/stacks-queue/SKILL.md +1 -1
- package/ai/skills/stacks-realtime/SKILL.md +1 -1
- package/ai/skills/stacks-registry/SKILL.md +1 -1
- package/ai/skills/stacks-repl/SKILL.md +1 -1
- package/ai/skills/stacks-retro/SKILL.md +121 -75
- package/ai/skills/stacks-review/SKILL.md +182 -74
- package/ai/skills/stacks-router/SKILL.md +1 -1
- package/ai/skills/stacks-routes/SKILL.md +1 -1
- package/ai/skills/stacks-scaffolding/SKILL.md +1 -1
- package/ai/skills/stacks-scheduler/SKILL.md +1 -1
- package/ai/skills/stacks-search-engine/SKILL.md +1 -1
- package/ai/skills/stacks-security/SKILL.md +1 -1
- package/ai/skills/stacks-security-audit/SKILL.md +1 -1
- package/ai/skills/stacks-server/SKILL.md +1 -1
- package/ai/skills/stacks-shell/SKILL.md +1 -1
- package/ai/skills/stacks-slug/SKILL.md +1 -1
- package/ai/skills/stacks-sms/SKILL.md +1 -1
- package/ai/skills/stacks-socials/SKILL.md +1 -1
- package/ai/skills/stacks-storage/SKILL.md +1 -1
- package/ai/skills/stacks-strings/SKILL.md +1 -1
- package/ai/skills/stacks-stx/SKILL.md +1 -1
- package/ai/skills/stacks-tdd/EXAMPLES.md +136 -0
- package/ai/skills/stacks-tdd/SKILL.md +125 -0
- package/ai/skills/stacks-testing/SKILL.md +13 -3
- package/ai/skills/stacks-tunnel/SKILL.md +1 -1
- package/ai/skills/stacks-types/SKILL.md +1 -1
- package/ai/skills/stacks-ui/SKILL.md +1 -1
- package/ai/skills/stacks-utils/SKILL.md +1 -1
- package/ai/skills/stacks-validation/SKILL.md +1 -1
- package/ai/skills/stacks-whois/SKILL.md +1 -1
- package/ai/skills/stacks-wizard/SKILL.md +127 -0
- package/ai/skills/stacks-wizard/scripts/template.sh +208 -0
- package/ai/skills/stacks-writing-for-agents/MECHANICS.md +125 -0
- package/ai/skills/stacks-writing-for-agents/SKILL.md +218 -0
- package/app/Actions/Auth/GenerateTwoFactorSecretAction.ts +12 -2
- package/app/Actions/Commerce/Shipping/{DriverDestroyAction.ts → CourierDestroyAction.ts} +6 -6
- package/app/Actions/Commerce/Shipping/{DriverIndexAction.ts → CourierIndexAction.ts} +3 -3
- package/app/Actions/Commerce/Shipping/CourierPingStoreAction.ts +61 -0
- package/app/Actions/Commerce/Shipping/{DriverShowAction.ts → CourierShowAction.ts} +5 -5
- package/app/Actions/Commerce/Shipping/{DriverStoreAction.ts → CourierStoreAction.ts} +4 -4
- package/app/Actions/Commerce/Shipping/{DriverUpdateAction.ts → CourierUpdateAction.ts} +6 -6
- package/app/Actions/Commerce/Shipping/DeliveryRouteStartAction.ts +38 -0
- package/app/Actions/Commerce/Shipping/DeliveryStopCompleteAction.ts +40 -0
- package/app/Actions/Commerce/Shipping/DeliveryStopFailAction.ts +44 -0
- package/app/Actions/Commerce/Shipping/DeliveryStopStartAction.ts +39 -0
- package/app/Actions/Commerce/Shipping/courier-session.ts +75 -0
- package/app/Actions/Commerce/commerce-action.test.ts +5 -5
- package/app/Actions/Dashboard/Commerce/CommerceDeliveryAction.ts +5 -5
- package/app/Actions/Dashboard/Commerce/CourierIndexAction.ts +24 -0
- package/app/Actions/Dashboard/Commerce/DeliveryRouteIndexAction.ts +7 -7
- package/app/Actions/Dashboard/Commerce/commerce-delivery.test.ts +11 -11
- package/app/Actions/Dashboard/Commerce/commerce-delivery.ts +29 -29
- package/app/Actions/Dashboard/Commerce/{driver-records.test.ts → courier-records.test.ts} +9 -9
- package/app/Actions/Dashboard/Commerce/{driver-records.ts → courier-records.ts} +14 -14
- package/app/Actions/Dashboard/Commerce/delivery-route-records.test.ts +13 -13
- package/app/Actions/Dashboard/Commerce/delivery-route-records.ts +25 -25
- package/app/Models/User.ts +1 -1
- package/app/Models/commerce/{Driver.ts → Courier.ts} +7 -7
- package/app/Models/commerce/{DriverPing.ts → CourierPing.ts} +7 -7
- package/app/Models/commerce/DeliveryRoute.ts +6 -6
- package/app/Models/commerce/DeliveryStop.ts +31 -8
- package/bootstrap.ts +7 -0
- package/functions/commerce/shippings/couriers.ts +19 -0
- package/ide/vscode/package.json +1 -1
- package/package.json +4 -3
- package/resources/components/Dashboard/Commerce/Delivery/{DriverDeleteDialog.stx → CourierDeleteDialog.stx} +5 -5
- package/resources/components/Dashboard/Commerce/Delivery/{DriverDialog.stx → CourierDialog.stx} +10 -10
- package/resources/components/Dashboard/Commerce/Delivery/{DriversDashboard.stx → CouriersDashboard.stx} +43 -43
- package/resources/components/Dashboard/Commerce/Delivery/{DriversTable.stx → CouriersTable.stx} +21 -21
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryOverviewDashboard.stx +18 -18
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryRouteDeleteDialog.stx +2 -2
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryRouteDialog.stx +22 -22
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryRoutesDashboard.stx +24 -24
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryRoutesTable.stx +8 -8
- package/resources/components/Dashboard/Commerce/Delivery/TabNavigation.stx +1 -1
- package/resources/functions/dashboard/data.ts +1 -1
- package/resources/functions/dashboard/sidebar.ts +2 -2
- package/routes/dashboard-api.ts +6 -6
- package/routes/dashboard.ts +7 -7
- package/routes/delivery.ts +24 -0
- package/types/defaults.ts +3 -3
- package/views/dashboard/.discovered-models.json +18 -18
- package/views/dashboard/AUDIT.md +1 -1
- package/views/dashboard/commerce/delivery/{drivers.stx → couriers.stx} +2 -2
- package/views/dashboard/composables/useChart.ts +16 -2
- package/app/Actions/Dashboard/Commerce/DriverIndexAction.ts +0 -24
- package/functions/commerce/shippings/drivers.ts +0 -19
package/ide/vscode/package.json
CHANGED
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/defaults",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.73.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/stacksjs/stacks.git",
|
|
@@ -51,8 +51,9 @@
|
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@iconify-json/f7": "^1.2.2",
|
|
53
53
|
"@iconify-json/hugeicons": "^1.2.27",
|
|
54
|
-
"@stacksjs/mobile": "^0.
|
|
55
|
-
"@stacksjs/sanitizer": "^0.2.113"
|
|
54
|
+
"@stacksjs/mobile": "^0.73.0",
|
|
55
|
+
"@stacksjs/sanitizer": "^0.2.113",
|
|
56
|
+
"ts-qr-codes": "^0.1.8"
|
|
56
57
|
},
|
|
57
58
|
"scripts": {
|
|
58
59
|
"build": "bun build.ts"
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
<script client>
|
|
2
|
-
interface
|
|
2
|
+
interface CourierRecord {
|
|
3
3
|
id: number
|
|
4
4
|
name: string
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
const open = useReactiveProp('open', false)
|
|
8
|
-
const record = useReactiveProp('record', null as
|
|
8
|
+
const record = useReactiveProp('record', null as CourierRecord | null)
|
|
9
9
|
const busy = useReactiveProp('busy', false)
|
|
10
10
|
const error = useReactiveProp('error', '')
|
|
11
11
|
const emit = defineEmits()
|
|
12
|
-
const description = derived(() => `Delete ${record()?.name || 'the selected
|
|
12
|
+
const description = derived(() => `Delete ${record()?.name || 'the selected courier'} permanently. Couriers referenced by delivery routes may be protected by the database.`)
|
|
13
13
|
</script>
|
|
14
14
|
|
|
15
15
|
<ConfirmDialog
|
|
16
16
|
:isOpen="open() && Boolean(record())"
|
|
17
|
-
title="Delete
|
|
17
|
+
title="Delete courier?"
|
|
18
18
|
:description="description()"
|
|
19
|
-
confirmLabel="Delete
|
|
19
|
+
confirmLabel="Delete courier"
|
|
20
20
|
:busy="busy()"
|
|
21
21
|
:error="error()"
|
|
22
22
|
@close="emit('close')"
|
package/resources/components/Dashboard/Commerce/Delivery/{DriverDialog.stx → CourierDialog.stx}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script client>
|
|
2
|
-
type
|
|
2
|
+
type CourierStatus = 'active' | 'on_delivery' | 'on_break'
|
|
3
3
|
|
|
4
4
|
interface UserOption {
|
|
5
5
|
id: number
|
|
@@ -7,19 +7,19 @@ interface UserOption {
|
|
|
7
7
|
email?: string
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
interface
|
|
10
|
+
interface CourierRecord {
|
|
11
11
|
id: number
|
|
12
12
|
name: string
|
|
13
13
|
phone: string
|
|
14
14
|
vehicle_number: string
|
|
15
15
|
license: string
|
|
16
|
-
status:
|
|
16
|
+
status: CourierStatus
|
|
17
17
|
user_id?: number | null
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const open = useReactiveProp('open', false)
|
|
21
21
|
const mode = useReactiveProp('mode', 'create')
|
|
22
|
-
const record = useReactiveProp('record', null as
|
|
22
|
+
const record = useReactiveProp('record', null as CourierRecord | null)
|
|
23
23
|
const users = useReactiveProp('users', [] as UserOption[])
|
|
24
24
|
const busy = useReactiveProp('busy', false)
|
|
25
25
|
const error = useReactiveProp('error', '')
|
|
@@ -28,7 +28,7 @@ const name = state('')
|
|
|
28
28
|
const phone = state('')
|
|
29
29
|
const vehicleNumber = state('')
|
|
30
30
|
const license = state('')
|
|
31
|
-
const status = state<
|
|
31
|
+
const status = state<CourierStatus>('active')
|
|
32
32
|
const userId = state('')
|
|
33
33
|
let hydrationKey = ''
|
|
34
34
|
|
|
@@ -53,7 +53,7 @@ effect(() => {
|
|
|
53
53
|
function submit(): void {
|
|
54
54
|
const selectedUserId = userId() ? Number(userId()) : null
|
|
55
55
|
if (!name().trim()) {
|
|
56
|
-
emit('invalid', '
|
|
56
|
+
emit('invalid', 'Courier name is required.')
|
|
57
57
|
return
|
|
58
58
|
}
|
|
59
59
|
if (!phone().trim()) {
|
|
@@ -86,16 +86,16 @@ function submit(): void {
|
|
|
86
86
|
|
|
87
87
|
<Modal
|
|
88
88
|
:isOpen="open()"
|
|
89
|
-
:title="mode() === 'edit' ? 'Edit
|
|
89
|
+
:title="mode() === 'edit' ? 'Edit courier' : 'Add courier'"
|
|
90
90
|
description="Manage operational identity, vehicle assignment, availability, and the optional User relationship."
|
|
91
91
|
size="md"
|
|
92
92
|
@close="emit('close')"
|
|
93
93
|
>
|
|
94
|
-
<form id="
|
|
94
|
+
<form id="courier-form" @submit.prevent="submit()">
|
|
95
95
|
<div :if="error()" class="mb-5 p-3 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">{{ error() }}</div>
|
|
96
96
|
|
|
97
97
|
<div class="space-y-4">
|
|
98
|
-
<label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">
|
|
98
|
+
<label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Courier name</span><input x-model="name" type="text" required maxlength="255" autocomplete="name" placeholder="Alex Morgan" class="mt-1 px-3 py-2 w-full text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-900 border border-gray-300 rounded-md dark:border-neutral-700" /></label>
|
|
99
99
|
<label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Phone</span><input x-model="phone" type="tel" required maxlength="255" autocomplete="tel" placeholder="+1 555 0100" class="mt-1 px-3 py-2 w-full text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-900 border border-gray-300 rounded-md dark:border-neutral-700" /></label>
|
|
100
100
|
<div class="grid gap-4 grid-cols-1 sm:grid-cols-2">
|
|
101
101
|
<label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Vehicle number</span><input x-model="vehicleNumber" type="text" required maxlength="255" autocomplete="off" placeholder="VAN-204" class="mt-1 px-3 py-2 w-full font-mono text-gray-900 text-sm uppercase dark:text-white bg-white dark:bg-neutral-900 border border-gray-300 rounded-md dark:border-neutral-700" /></label>
|
|
@@ -114,7 +114,7 @@ function submit(): void {
|
|
|
114
114
|
<template #footer>
|
|
115
115
|
<div class="flex gap-2 justify-end w-full">
|
|
116
116
|
<Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
|
|
117
|
-
<Button type="submit" form="
|
|
117
|
+
<Button type="submit" form="courier-form" :loading="busy()">{{ mode() === 'edit' ? 'Save changes' : 'Add courier' }}</Button>
|
|
118
118
|
</div>
|
|
119
119
|
</template>
|
|
120
120
|
</Modal>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { dashboardApi } from '../../../../../functions/dashboard-api'
|
|
3
3
|
import { pushToast } from '../../../../../functions/toasts'
|
|
4
4
|
|
|
5
|
-
type
|
|
5
|
+
type CourierStatus = 'active' | 'on_delivery' | 'on_break'
|
|
6
6
|
|
|
7
7
|
interface UserOption {
|
|
8
8
|
id: number
|
|
@@ -14,27 +14,27 @@ interface UserIndexResponse {
|
|
|
14
14
|
users: UserOption[]
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
interface
|
|
17
|
+
interface CourierRecord {
|
|
18
18
|
id: number
|
|
19
19
|
name: string
|
|
20
20
|
phone: string
|
|
21
21
|
vehicle_number: string
|
|
22
22
|
license: string
|
|
23
|
-
status:
|
|
23
|
+
status: CourierStatus
|
|
24
24
|
user_id?: number | null
|
|
25
25
|
user?: UserOption
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
interface
|
|
28
|
+
interface CourierPayload {
|
|
29
29
|
name: string
|
|
30
30
|
phone: string
|
|
31
31
|
vehicleNumber: string
|
|
32
32
|
license: string
|
|
33
|
-
status:
|
|
33
|
+
status: CourierStatus
|
|
34
34
|
userId: number | null
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
const records = state<
|
|
37
|
+
const records = state<CourierRecord[]>([])
|
|
38
38
|
const users = state<UserOption[]>([])
|
|
39
39
|
const loading = state(true)
|
|
40
40
|
const loadError = state('')
|
|
@@ -43,10 +43,10 @@ const page = state(1)
|
|
|
43
43
|
const perPage = state(10)
|
|
44
44
|
const editorOpen = state(false)
|
|
45
45
|
const editorMode = state<'create' | 'edit'>('create')
|
|
46
|
-
const editing = state<
|
|
46
|
+
const editing = state<CourierRecord | null>(null)
|
|
47
47
|
const saving = state(false)
|
|
48
48
|
const editorError = state('')
|
|
49
|
-
const deleting = state<
|
|
49
|
+
const deleting = state<CourierRecord | null>(null)
|
|
50
50
|
const deleteBusy = state(false)
|
|
51
51
|
const deleteError = state('')
|
|
52
52
|
|
|
@@ -54,14 +54,14 @@ function userFor(id?: number | null): UserOption | undefined {
|
|
|
54
54
|
return id ? users().find(user => user.id === id) : undefined
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
function withUser(record:
|
|
57
|
+
function withUser(record: CourierRecord): CourierRecord {
|
|
58
58
|
return {
|
|
59
59
|
...record,
|
|
60
60
|
user: userFor(record.user_id) || record.user,
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
function filteredRecords():
|
|
64
|
+
function filteredRecords(): CourierRecord[] {
|
|
65
65
|
const query = search().trim().toLowerCase()
|
|
66
66
|
if (!query)
|
|
67
67
|
return records()
|
|
@@ -76,43 +76,43 @@ function filteredRecords(): DriverRecord[] {
|
|
|
76
76
|
].some(value => String(value || '').toLowerCase().includes(query)))
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
function paginatedRecords():
|
|
79
|
+
function paginatedRecords(): CourierRecord[] {
|
|
80
80
|
const start = (page() - 1) * perPage()
|
|
81
81
|
return filteredRecords().slice(start, start + perPage())
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
function statusCount(status:
|
|
84
|
+
function statusCount(status: CourierStatus): number {
|
|
85
85
|
return records().filter(record => record.status === status).length
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
function metricCards(): Array<{ label: string, value: string, detail: string }> {
|
|
89
89
|
return [
|
|
90
|
-
{ label: '
|
|
90
|
+
{ label: 'Couriers', value: records().length.toLocaleString(), detail: 'Persisted Courier records' },
|
|
91
91
|
{ label: 'Available', value: statusCount('active').toLocaleString(), detail: 'Ready for assignment' },
|
|
92
92
|
{ label: 'On delivery', value: statusCount('on_delivery').toLocaleString(), detail: 'Currently operating' },
|
|
93
93
|
{ label: 'On break', value: statusCount('on_break').toLocaleString(), detail: `${records().filter(record => record.user_id).length} linked users` },
|
|
94
94
|
]
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
async function
|
|
97
|
+
async function loadCouriers(): Promise<void> {
|
|
98
98
|
loading.set(true)
|
|
99
99
|
loadError.set('')
|
|
100
100
|
try {
|
|
101
|
-
const [
|
|
102
|
-
dashboardApi<
|
|
101
|
+
const [courierData, userData] = await Promise.all([
|
|
102
|
+
dashboardApi<CourierRecord[]>('/api/dashboard/commerce/couriers'),
|
|
103
103
|
dashboardApi<UserIndexResponse>('/api/dashboard/data/users'),
|
|
104
104
|
])
|
|
105
|
-
if (!Array.isArray(
|
|
106
|
-
throw new TypeError('
|
|
105
|
+
if (!Array.isArray(courierData) || !Array.isArray(userData?.users))
|
|
106
|
+
throw new TypeError('Courier resources were not returned in the expected shape.')
|
|
107
107
|
users.set(userData.users)
|
|
108
|
-
records.set(
|
|
108
|
+
records.set(courierData.map(withUser))
|
|
109
109
|
const lastPage = Math.max(1, Math.ceil(filteredRecords().length / perPage()))
|
|
110
110
|
if (page() > lastPage)
|
|
111
111
|
page.set(lastPage)
|
|
112
112
|
}
|
|
113
113
|
catch (error) {
|
|
114
114
|
loadError.set(error instanceof Error ? error.message : String(error))
|
|
115
|
-
pushToast('error', 'Could not load
|
|
115
|
+
pushToast('error', 'Could not load couriers', { detail: loadError() })
|
|
116
116
|
}
|
|
117
117
|
finally {
|
|
118
118
|
loading.set(false)
|
|
@@ -131,7 +131,7 @@ function openCreate(): void {
|
|
|
131
131
|
editorOpen.set(true)
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
function openEdit(record:
|
|
134
|
+
function openEdit(record: CourierRecord): void {
|
|
135
135
|
editing.set(record)
|
|
136
136
|
editorMode.set('edit')
|
|
137
137
|
editorError.set('')
|
|
@@ -150,15 +150,15 @@ function setEditorError(message: string): void {
|
|
|
150
150
|
editorError.set(message)
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
async function
|
|
153
|
+
async function saveCourier(payload: CourierPayload): Promise<void> {
|
|
154
154
|
saving.set(true)
|
|
155
155
|
editorError.set('')
|
|
156
156
|
try {
|
|
157
157
|
const current = editing()
|
|
158
|
-
const updated = await dashboardApi<
|
|
158
|
+
const updated = await dashboardApi<CourierRecord>(
|
|
159
159
|
current
|
|
160
|
-
? `/api/dashboard/commerce/
|
|
161
|
-
: '/api/dashboard/commerce/
|
|
160
|
+
? `/api/dashboard/commerce/couriers/${current.id}`
|
|
161
|
+
: '/api/dashboard/commerce/couriers',
|
|
162
162
|
{
|
|
163
163
|
method: current ? 'PATCH' : 'POST',
|
|
164
164
|
body: payload,
|
|
@@ -175,7 +175,7 @@ async function saveDriver(payload: DriverPayload): Promise<void> {
|
|
|
175
175
|
}
|
|
176
176
|
editorOpen.set(false)
|
|
177
177
|
editing.set(null)
|
|
178
|
-
pushToast('success', current ? '
|
|
178
|
+
pushToast('success', current ? 'Courier updated' : 'Courier added', { detail: hydrated.name })
|
|
179
179
|
}
|
|
180
180
|
catch (error) {
|
|
181
181
|
editorError.set(error instanceof Error ? error.message : String(error))
|
|
@@ -185,7 +185,7 @@ async function saveDriver(payload: DriverPayload): Promise<void> {
|
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
function requestDelete(record:
|
|
188
|
+
function requestDelete(record: CourierRecord): void {
|
|
189
189
|
deleting.set(record)
|
|
190
190
|
deleteError.set('')
|
|
191
191
|
}
|
|
@@ -204,7 +204,7 @@ async function confirmDelete(): Promise<void> {
|
|
|
204
204
|
deleteBusy.set(true)
|
|
205
205
|
deleteError.set('')
|
|
206
206
|
try {
|
|
207
|
-
await dashboardApi(`/api/dashboard/commerce/
|
|
207
|
+
await dashboardApi(`/api/dashboard/commerce/couriers/${current.id}`, {
|
|
208
208
|
method: 'DELETE',
|
|
209
209
|
})
|
|
210
210
|
records.set(records().filter(record => record.id !== current.id))
|
|
@@ -212,7 +212,7 @@ async function confirmDelete(): Promise<void> {
|
|
|
212
212
|
const lastPage = Math.max(1, Math.ceil(filteredRecords().length / perPage()))
|
|
213
213
|
if (page() > lastPage)
|
|
214
214
|
page.set(lastPage)
|
|
215
|
-
pushToast('success', '
|
|
215
|
+
pushToast('success', 'Courier deleted', { detail: current.name })
|
|
216
216
|
}
|
|
217
217
|
catch (error) {
|
|
218
218
|
deleteError.set(error instanceof Error ? error.message : String(error))
|
|
@@ -227,42 +227,42 @@ function goToPage(value: number): void {
|
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
onMount(() => {
|
|
230
|
-
void
|
|
230
|
+
void loadCouriers()
|
|
231
231
|
})
|
|
232
232
|
</script>
|
|
233
233
|
|
|
234
234
|
<div class="space-y-6">
|
|
235
|
-
<TabNavigation model-value="
|
|
235
|
+
<TabNavigation model-value="couriers" />
|
|
236
236
|
|
|
237
237
|
<header class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
|
238
238
|
<div>
|
|
239
|
-
<h1 class="font-bold text-2xl text-gray-900 dark:text-white">Delivery
|
|
240
|
-
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Manage
|
|
239
|
+
<h1 class="font-bold text-2xl text-gray-900 dark:text-white">Delivery couriers</h1>
|
|
240
|
+
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Manage courier availability, vehicles, licenses, and optional user accounts.</p>
|
|
241
241
|
</div>
|
|
242
242
|
<div class="flex gap-2">
|
|
243
|
-
<Button :loading="loading()" variant="secondary" @click="
|
|
244
|
-
<Button :disabled="loading()" @click="openCreate()"><span aria-hidden="true" class="h-4 w-4 i-hugeicons-add-01"></span>Add
|
|
243
|
+
<Button :loading="loading()" variant="secondary" @click="loadCouriers()"><span :if="!loading()" aria-hidden="true" class="h-4 w-4 i-hugeicons-refresh"></span>Refresh</Button>
|
|
244
|
+
<Button :disabled="loading()" @click="openCreate()"><span aria-hidden="true" class="h-4 w-4 i-hugeicons-add-01"></span>Add courier</Button>
|
|
245
245
|
</div>
|
|
246
246
|
</header>
|
|
247
247
|
|
|
248
|
-
<div :if="loadError()" class="flex gap-4 items-center justify-between 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"><span>{{ loadError() }}</span><Button variant="secondary" size="sm" @click="
|
|
248
|
+
<div :if="loadError()" class="flex gap-4 items-center justify-between 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"><span>{{ loadError() }}</span><Button variant="secondary" size="sm" @click="loadCouriers()">Retry</Button></div>
|
|
249
249
|
|
|
250
|
-
<section aria-label="
|
|
250
|
+
<section aria-label="Courier metrics" class="grid gap-3 grid-cols-1 sm:grid-cols-2 xl:grid-cols-4">
|
|
251
251
|
<template :for="item in metricCards()" :key="item.label"><article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">{{ item.label }}</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ item.value }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">{{ item.detail }}</p></article></template>
|
|
252
252
|
</section>
|
|
253
253
|
|
|
254
254
|
<section class="overflow-hidden bg-white dark:bg-neutral-800 border border-gray-200 rounded-xl dark:border-neutral-700">
|
|
255
255
|
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between px-4 py-4 border-b border-gray-200 dark:border-neutral-700">
|
|
256
|
-
<div><h2 class="font-semibold text-gray-900 dark:text-white">
|
|
257
|
-
<SearchFilter placeholder="Search
|
|
256
|
+
<div><h2 class="font-semibold text-gray-900 dark:text-white">Courier roster</h2><p class="mt-0.5 text-gray-500 text-xs dark:text-neutral-400">{{ filteredRecords().length }} matching records</p></div>
|
|
257
|
+
<SearchFilter placeholder="Search couriers" class="w-full sm:w-80" @search="handleSearch" />
|
|
258
258
|
</div>
|
|
259
|
-
<div :if="loading()" class="flex items-center justify-center py-24 text-gray-500 text-sm dark:text-neutral-400"><span class="mr-2 h-4 w-4 animate-spin i-hugeicons-loading-03"></span>Loading
|
|
260
|
-
<
|
|
259
|
+
<div :if="loading()" class="flex items-center justify-center py-24 text-gray-500 text-sm dark:text-neutral-400"><span class="mr-2 h-4 w-4 animate-spin i-hugeicons-loading-03"></span>Loading couriers</div>
|
|
260
|
+
<CouriersTable :if="!loading()" :couriers="paginatedRecords()" @edit="openEdit" @delete="requestDelete" />
|
|
261
261
|
<div :if="!loading()" class="px-4 py-3 bg-gray-50 dark:bg-neutral-900/50 border-gray-200 border-t dark:border-neutral-700">
|
|
262
262
|
<DeliveryPagination :current-page="page()" :total-items="filteredRecords().length" :items-per-page="perPage()" @select="goToPage" />
|
|
263
263
|
</div>
|
|
264
264
|
</section>
|
|
265
265
|
</div>
|
|
266
266
|
|
|
267
|
-
<
|
|
268
|
-
<
|
|
267
|
+
<CourierDialog :open="editorOpen()" :mode="editorMode()" :record="editing()" :users="users()" :busy="saving()" :error="editorError()" @submit="saveCourier" @invalid="setEditorError" @close="closeEditor()" />
|
|
268
|
+
<CourierDeleteDialog :open="Boolean(deleting())" :record="deleting()" :busy="deleteBusy()" :error="deleteError()" @confirm="confirmDelete()" @close="closeDelete()" />
|
package/resources/components/Dashboard/Commerce/Delivery/{DriversTable.stx → CouriersTable.stx}
RENAMED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
<script client>
|
|
2
|
-
type
|
|
2
|
+
type CourierStatus = 'active' | 'on_delivery' | 'on_break'
|
|
3
3
|
|
|
4
|
-
interface
|
|
4
|
+
interface CourierRow {
|
|
5
5
|
id: number
|
|
6
6
|
name: string
|
|
7
7
|
phone: string
|
|
8
8
|
vehicle_number: string
|
|
9
9
|
license: string
|
|
10
|
-
status:
|
|
10
|
+
status: CourierStatus
|
|
11
11
|
user_id?: number | null
|
|
12
12
|
user?: { id: number, name: string, email?: string }
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
const
|
|
15
|
+
const couriers = useReactiveProp('couriers', [] as CourierRow[])
|
|
16
16
|
const emit = defineEmits()
|
|
17
17
|
|
|
18
|
-
function statusLabel(status:
|
|
18
|
+
function statusLabel(status: CourierStatus): string {
|
|
19
19
|
return {
|
|
20
20
|
active: 'Available',
|
|
21
21
|
on_delivery: 'On delivery',
|
|
@@ -23,7 +23,7 @@ function statusLabel(status: DriverStatus): string {
|
|
|
23
23
|
}[status] || status
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
function statusClass(status:
|
|
26
|
+
function statusClass(status: CourierStatus): string {
|
|
27
27
|
if (status === 'active')
|
|
28
28
|
return 'bg-green-50 text-green-700 ring-green-600/20 dark:bg-green-900/30 dark:text-green-300'
|
|
29
29
|
if (status === 'on_delivery')
|
|
@@ -36,7 +36,7 @@ function statusClass(status: DriverStatus): string {
|
|
|
36
36
|
<table class="min-w-full divide-stone-100 divide-y dark:divide-neutral-700">
|
|
37
37
|
<thead class="bg-stone-50/80 dark:bg-neutral-800">
|
|
38
38
|
<tr>
|
|
39
|
-
<th scope="col" class="pl-6 pr-3 py-3.5 font-semibold text-left text-stone-500 text-xs tracking-wider uppercase dark:text-neutral-400">
|
|
39
|
+
<th scope="col" class="pl-6 pr-3 py-3.5 font-semibold text-left text-stone-500 text-xs tracking-wider uppercase dark:text-neutral-400">Courier</th>
|
|
40
40
|
<th scope="col" class="px-4 py-3.5 font-semibold text-left text-stone-500 text-xs tracking-wider uppercase dark:text-neutral-400">Phone</th>
|
|
41
41
|
<th scope="col" class="px-4 py-3.5 font-semibold text-left text-stone-500 text-xs tracking-wider uppercase dark:text-neutral-400">Vehicle</th>
|
|
42
42
|
<th scope="col" class="px-4 py-3.5 font-semibold text-left text-stone-500 text-xs tracking-wider uppercase dark:text-neutral-400">License</th>
|
|
@@ -48,26 +48,26 @@ function statusClass(status: DriverStatus): string {
|
|
|
48
48
|
</tr>
|
|
49
49
|
</thead>
|
|
50
50
|
<tbody class="bg-white dark:bg-neutral-800 divide-stone-100 divide-y dark:divide-neutral-700">
|
|
51
|
-
<template :for="
|
|
51
|
+
<template :for="courier in couriers" :key="courier.id">
|
|
52
52
|
<tr class="hover:bg-stone-50/50 dark:hover:bg-neutral-700/50 transition-colors">
|
|
53
53
|
<td class="pl-6 pr-3 py-3.5 font-medium text-sm text-stone-800 whitespace-nowrap dark:text-white">
|
|
54
|
-
{{
|
|
54
|
+
{{ courier.name }}
|
|
55
55
|
</td>
|
|
56
56
|
<td class="px-4 py-3.5 text-sm text-stone-500 whitespace-nowrap dark:text-neutral-300">
|
|
57
|
-
{{
|
|
57
|
+
{{ courier.phone }}
|
|
58
58
|
</td>
|
|
59
59
|
<td class="px-4 py-3.5 font-mono text-sm text-stone-500 whitespace-nowrap dark:text-neutral-300">
|
|
60
|
-
{{
|
|
60
|
+
{{ courier.vehicle_number }}
|
|
61
61
|
</td>
|
|
62
62
|
<td class="px-4 py-3.5 font-mono text-sm text-stone-500 whitespace-nowrap dark:text-neutral-300">
|
|
63
|
-
{{
|
|
63
|
+
{{ courier.license }}
|
|
64
64
|
</td>
|
|
65
65
|
<td class="px-4 py-3.5 text-sm text-stone-500 whitespace-nowrap dark:text-neutral-300">
|
|
66
|
-
{{
|
|
66
|
+
{{ courier.user?.name || (courier.user_id ? 'User #' + courier.user_id : 'Not linked') }}
|
|
67
67
|
</td>
|
|
68
68
|
<td class="px-4 py-3.5 text-sm whitespace-nowrap">
|
|
69
|
-
<span :class="'inline-flex items-center px-2 py-1 font-medium text-xs ring-1 ring-inset rounded-md ' + statusClass(
|
|
70
|
-
{{ statusLabel(
|
|
69
|
+
<span :class="'inline-flex items-center px-2 py-1 font-medium text-xs ring-1 ring-inset rounded-md ' + statusClass(courier.status)">
|
|
70
|
+
{{ statusLabel(courier.status) }}
|
|
71
71
|
</span>
|
|
72
72
|
</td>
|
|
73
73
|
<td class="relative pl-3 pr-6 py-3.5 font-medium text-right text-sm whitespace-nowrap">
|
|
@@ -75,8 +75,8 @@ function statusClass(status: DriverStatus): string {
|
|
|
75
75
|
variant="ghost"
|
|
76
76
|
size="sm"
|
|
77
77
|
iconOnly
|
|
78
|
-
@click="emit('edit',
|
|
79
|
-
:ariaLabel="'Edit
|
|
78
|
+
@click="emit('edit', courier)"
|
|
79
|
+
:ariaLabel="'Edit courier ' + courier.name + ', record ' + courier.id"
|
|
80
80
|
>
|
|
81
81
|
<span class="block h-4 w-4 text-blue-500 i-hugeicons-edit-01"></span>
|
|
82
82
|
</Button>
|
|
@@ -84,8 +84,8 @@ function statusClass(status: DriverStatus): string {
|
|
|
84
84
|
variant="ghost"
|
|
85
85
|
size="sm"
|
|
86
86
|
iconOnly
|
|
87
|
-
@click="emit('delete',
|
|
88
|
-
:ariaLabel="'Delete
|
|
87
|
+
@click="emit('delete', courier)"
|
|
88
|
+
:ariaLabel="'Delete courier ' + courier.name + ', record ' + courier.id"
|
|
89
89
|
>
|
|
90
90
|
<span class="block h-4 w-4 text-red-500 i-hugeicons-waste"></span>
|
|
91
91
|
</Button>
|
|
@@ -93,9 +93,9 @@ function statusClass(status: DriverStatus): string {
|
|
|
93
93
|
</tr>
|
|
94
94
|
</template>
|
|
95
95
|
|
|
96
|
-
<tr :if="
|
|
96
|
+
<tr :if="couriers().length === 0">
|
|
97
97
|
<td colspan="7" class="px-6 py-12 text-center text-sm text-stone-500 dark:text-neutral-400">
|
|
98
|
-
No
|
|
98
|
+
No couriers found.
|
|
99
99
|
</td>
|
|
100
100
|
</tr>
|
|
101
101
|
</tbody>
|
|
@@ -10,7 +10,7 @@ interface DeliveryStat {
|
|
|
10
10
|
|
|
11
11
|
interface DeliveryRoute {
|
|
12
12
|
id: number
|
|
13
|
-
|
|
13
|
+
courier: string
|
|
14
14
|
vehicle: string
|
|
15
15
|
stops: number
|
|
16
16
|
duration: string
|
|
@@ -34,7 +34,7 @@ interface ShippingZone {
|
|
|
34
34
|
regions: number
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
interface
|
|
37
|
+
interface CourierSummary {
|
|
38
38
|
total: number
|
|
39
39
|
active: number
|
|
40
40
|
onDelivery: number
|
|
@@ -46,14 +46,14 @@ interface DeliveryOverviewResponse {
|
|
|
46
46
|
routes: DeliveryRoute[]
|
|
47
47
|
methods: ShippingMethod[]
|
|
48
48
|
zones: ShippingZone[]
|
|
49
|
-
|
|
49
|
+
couriers: CourierSummary
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
const stats = state<DeliveryStat[]>([])
|
|
53
53
|
const routes = state<DeliveryRoute[]>([])
|
|
54
54
|
const methods = state<ShippingMethod[]>([])
|
|
55
55
|
const zones = state<ShippingZone[]>([])
|
|
56
|
-
const
|
|
56
|
+
const couriers = state<CourierSummary>({
|
|
57
57
|
total: 0,
|
|
58
58
|
active: 0,
|
|
59
59
|
onDelivery: 0,
|
|
@@ -65,7 +65,7 @@ const loadError = state('')
|
|
|
65
65
|
function statIcon(label: string): string {
|
|
66
66
|
const icons: Record<string, string> = {
|
|
67
67
|
'Active Routes': 'i-hugeicons-route-02',
|
|
68
|
-
'
|
|
68
|
+
'Couriers on Delivery': 'i-hugeicons-delivery-truck-02',
|
|
69
69
|
'Average Route': 'i-hugeicons-clock-01',
|
|
70
70
|
'Active Zones': 'i-hugeicons-maps-global-02',
|
|
71
71
|
}
|
|
@@ -114,7 +114,7 @@ async function loadOverview(): Promise<void> {
|
|
|
114
114
|
|| !Array.isArray(data.routes)
|
|
115
115
|
|| !Array.isArray(data.methods)
|
|
116
116
|
|| !Array.isArray(data.zones)
|
|
117
|
-
|| !data.
|
|
117
|
+
|| !data.couriers
|
|
118
118
|
) {
|
|
119
119
|
throw new TypeError('Delivery resources were not returned in the expected shape.')
|
|
120
120
|
}
|
|
@@ -123,7 +123,7 @@ async function loadOverview(): Promise<void> {
|
|
|
123
123
|
routes.set(data.routes)
|
|
124
124
|
methods.set(data.methods)
|
|
125
125
|
zones.set(data.zones)
|
|
126
|
-
|
|
126
|
+
couriers.set(data.couriers)
|
|
127
127
|
}
|
|
128
128
|
catch (error) {
|
|
129
129
|
loadError.set(error instanceof Error ? error.message : String(error))
|
|
@@ -145,7 +145,7 @@ onMount(() => {
|
|
|
145
145
|
<header class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
|
146
146
|
<div>
|
|
147
147
|
<h1 class="font-bold text-2xl text-gray-900 dark:text-white">Delivery</h1>
|
|
148
|
-
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Monitor active routes,
|
|
148
|
+
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Monitor active routes, courier availability, shipping methods, and coverage.</p>
|
|
149
149
|
</div>
|
|
150
150
|
<div class="flex gap-2">
|
|
151
151
|
<Button :loading="loading()" variant="secondary" @click="loadOverview()">
|
|
@@ -211,7 +211,7 @@ onMount(() => {
|
|
|
211
211
|
<table class="min-w-full divide-gray-200 divide-y dark:divide-neutral-700">
|
|
212
212
|
<thead class="bg-gray-50 dark:bg-neutral-900/40">
|
|
213
213
|
<tr>
|
|
214
|
-
<th scope="col" class="px-5 py-3 font-medium text-gray-500 text-left text-xs tracking-wide uppercase dark:text-neutral-400">
|
|
214
|
+
<th scope="col" class="px-5 py-3 font-medium text-gray-500 text-left text-xs tracking-wide uppercase dark:text-neutral-400">Courier</th>
|
|
215
215
|
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-left text-xs tracking-wide uppercase dark:text-neutral-400">Plan</th>
|
|
216
216
|
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-left text-xs tracking-wide uppercase dark:text-neutral-400">Distance</th>
|
|
217
217
|
<th scope="col" class="px-5 py-3 font-medium text-gray-500 text-right text-xs tracking-wide uppercase dark:text-neutral-400">Last active</th>
|
|
@@ -221,7 +221,7 @@ onMount(() => {
|
|
|
221
221
|
<template :for="route in routes()" :key="route.id">
|
|
222
222
|
<tr class="hover:bg-gray-50/70 dark:hover:bg-neutral-700/40">
|
|
223
223
|
<td class="px-5 py-3.5">
|
|
224
|
-
<p class="font-medium text-gray-900 text-sm dark:text-white">{{ route.
|
|
224
|
+
<p class="font-medium text-gray-900 text-sm dark:text-white">{{ route.courier }}</p>
|
|
225
225
|
<p class="mt-0.5 text-gray-500 text-xs dark:text-neutral-400">{{ route.vehicle }}</p>
|
|
226
226
|
</td>
|
|
227
227
|
<td class="px-4 py-3.5 text-gray-600 text-sm whitespace-nowrap dark:text-neutral-300">
|
|
@@ -243,30 +243,30 @@ onMount(() => {
|
|
|
243
243
|
<div class="flex items-start justify-between">
|
|
244
244
|
<div>
|
|
245
245
|
<h2 class="font-semibold">Fleet availability</h2>
|
|
246
|
-
<p class="mt-1 text-sm text-stone-400">Current persisted
|
|
246
|
+
<p class="mt-1 text-sm text-stone-400">Current persisted courier statuses.</p>
|
|
247
247
|
</div>
|
|
248
248
|
<span class="h-5 w-5 text-blue-400 i-hugeicons-delivery-truck-02"></span>
|
|
249
249
|
</div>
|
|
250
250
|
<div class="mt-7">
|
|
251
|
-
<p class="font-semibold tabular-nums text-4xl tracking-tight">{{
|
|
252
|
-
<p class="mt-1 text-sm text-stone-400">
|
|
251
|
+
<p class="font-semibold tabular-nums text-4xl tracking-tight">{{ couriers().total }}</p>
|
|
252
|
+
<p class="mt-1 text-sm text-stone-400">Couriers in the fleet</p>
|
|
253
253
|
</div>
|
|
254
254
|
<dl class="mt-7 space-y-3">
|
|
255
255
|
<div class="flex items-center justify-between py-2 border-b border-stone-800">
|
|
256
256
|
<dt class="flex gap-2 items-center text-sm text-stone-300"><span class="h-2 w-2 bg-emerald-400 rounded-full"></span>Available</dt>
|
|
257
|
-
<dd class="font-semibold tabular-nums">{{
|
|
257
|
+
<dd class="font-semibold tabular-nums">{{ couriers().active }}</dd>
|
|
258
258
|
</div>
|
|
259
259
|
<div class="flex items-center justify-between py-2 border-b border-stone-800">
|
|
260
260
|
<dt class="flex gap-2 items-center text-sm text-stone-300"><span class="h-2 w-2 bg-blue-400 rounded-full"></span>On delivery</dt>
|
|
261
|
-
<dd class="font-semibold tabular-nums">{{
|
|
261
|
+
<dd class="font-semibold tabular-nums">{{ couriers().onDelivery }}</dd>
|
|
262
262
|
</div>
|
|
263
263
|
<div class="flex items-center justify-between py-2">
|
|
264
264
|
<dt class="flex gap-2 items-center text-sm text-stone-300"><span class="h-2 w-2 bg-amber-400 rounded-full"></span>On break</dt>
|
|
265
|
-
<dd class="font-semibold tabular-nums">{{
|
|
265
|
+
<dd class="font-semibold tabular-nums">{{ couriers().onBreak }}</dd>
|
|
266
266
|
</div>
|
|
267
267
|
</dl>
|
|
268
|
-
<a href="/commerce/delivery/
|
|
269
|
-
Manage
|
|
268
|
+
<a href="/commerce/delivery/couriers" class="inline-flex gap-2 items-center mt-6 font-semibold text-blue-300 text-sm hover:text-blue-200">
|
|
269
|
+
Manage couriers
|
|
270
270
|
<span class="h-4 w-4 i-hugeicons-arrow-right-01"></span>
|
|
271
271
|
</a>
|
|
272
272
|
</section>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script client>
|
|
2
2
|
interface DeliveryRouteRecord {
|
|
3
3
|
id: number
|
|
4
|
-
|
|
4
|
+
courier: string
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
const open = useReactiveProp('open', false)
|
|
@@ -9,7 +9,7 @@ const record = useReactiveProp('record', null as DeliveryRouteRecord | null)
|
|
|
9
9
|
const busy = useReactiveProp('busy', false)
|
|
10
10
|
const error = useReactiveProp('error', '')
|
|
11
11
|
const emit = defineEmits()
|
|
12
|
-
const description = derived(() => `Delete route #${record()?.id} for ${record()?.
|
|
12
|
+
const description = derived(() => `Delete route #${record()?.id} for ${record()?.courier || 'the selected courier'} permanently.`)
|
|
13
13
|
</script>
|
|
14
14
|
|
|
15
15
|
<ConfirmDialog
|