镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Commit cfae0e6

Browse files
fix(dashboard): delivery routes are searched, sorted, paged and summarised by the database
The Delivery Routes page read the 500 most recently active routes and did everything in the browser, so an older route could not be found, edited or deleted from it, and its cards (routes, linked couriers, stops, distance, active in 24h) counted those 500. GET /api/dashboard/commerce/delivery-routes now takes q, sort, direction, page and per_page, refuses a bad value with a 422, and answers one page with how many routes match and a summary of every route. Search covers the id, the recorded courier and the linked courier's name, the vehicle, and the stops, time and distance as text, ignoring case, with LIKE wildcards escaped. The default order is most recently active first, `last_active` compared as text: Unix milliseconds and seconds from different seconds already compare that way in time order. Ties read most recently active first, then by id. Couriers are read by exactly the ids the page references. "Active in 24h" uses the overview's window (40738ed) and its exact cut. The page client is shared now. useServerList (functions/dashboard-list.ts) is the request counter, page, page size, header sort and search debounce that the Orders, Payments, Products, Gift Cards, Reviews and waitlist pages each wrote by hand: only the newest answer lands, a search or filter change asks for page 1, and a header the server cannot sort by returns to the default order. The routes page uses it, keeps useRecordsDashboard for the dialog and the delete confirmation, and reloads the page after a save or delete instead of patching a local copy. The endpoint used to return a bare array, which the auto-imported useDeliveryRoutes().fetchDeliveryRoutes read as "every route". createDashboardResource takes `paged: true` for it, and fetchAll then reads every page through readEveryPage, so it returns every route rather than the first 500. Tested on SQLite, PostgreSQL and MySQL with 520 routes and couriers, every fiftieth stored as Unix seconds. Controls went red: reading the first 500 couriers ("DeliveryRoute 4 references missing Courier 505"), dropping useServerList's newest-answer guard, and dropping readEveryPage's count-reached stop. A control that removed padding seconds to milliseconds stayed green; the padding did nothing, so it is gone and the comment says why. Not verified live: the dev database's delivery_routes has no courier_id and there is no couriers table, so this endpoint answers 503 there, as the page did before, which loaded couriers and failed on them. Refs #3218
1 parent 3ae1c09 commit cfae0e6

14 files changed

Lines changed: 814 additions & 80 deletions

File tree

‎storage/framework/auto-imports/RUNTIME.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ If boot reports a failed package or barrel, its names are unavailable until it l
4040
| `@stacksjs/i18n` | `I18n`, `getLocale`, `setLocale`, `t`, `tc`, `te` |
4141
| `@stacksjs/stx` | `derived`, `effect`, `state` |
4242
| `@stacksjs/browser` | `useDark`, `usePreferredDark`, `useStorage`, `useToggle` |
43-
| `auto-imports/functions.ts` | `DashboardApiEnvironmentError`, `DashboardApiError`, `applyMigrationOperations`, `askBuddy`, `authGuard`, `badgeClassToggles`, `badgeClasses`, `badgeDotClassToggles`, `badgeDotClasses`, `badgeShowsDot`, `buttonVariantClasses`, `buttonVariantToggles`, `clearAllToasts`, `clearBuddyChat`, `closeTerminalSession`, `count`, `createDashboardResource`, `createRecoveryDestination`, `createRecoveryPolicy`, `createResource`, `dashboardApi`, `dashboardDownload`, `dashboardStream`, `decideRelease`, `decrement`, `del`, `deleteBlogPost`, `deleteInboxEmail`, `describeAuthError`, `dismissToast`, `downloadDashboardInboxAttachment`, `ensureCraftNativeSidebarMarker`, `errorGroupPageParams`, `errorGroupParams`, `fetchAuditOperations`, `fetchBillingOverview`, `fetchBlogPost`, `fetchBlogPosts`, `fetchBuddyChat`, `fetchCampaignAnalytics`, `fetchCapturedMail`, `fetchCapturedMailMessage`, `fetchChangeOperations`, `fetchDashboardData`, `fetchDashboardHome`, `fetchDashboardInbox`, `fetchDashboardInboxBody`, `fetchDashboardQueries`, `fetchDashboardQuery`, `fetchDeployScript`, `fetchDeployment`, `fetchDeploymentTerminal`, `fetchDeployments`, `fetchEventAnalytics`, `fetchGlobalSearch`, `fetchInboxActivity`, `fetchIncidentOperations`, `fetchJob`, `fetchJobDefinitions`, `fetchJobStats`, `fetchJobs`, `fetchMailPreference`, `fetchMigrationOperations`, `fetchNotificationDeliveries`, `fetchNotificationDeliveryHistory`, `fetchNotificationDeliveryOverview`, `fetchQueueStats`, `fetchQueueWorkers`, `fetchRealtimeDashboard`, `fetchRecoveryOperations`, `fetchRemoteRegistry`, `fetchSalesAnalytics`, `fetchSchedulerOperations`, `fetchSearchEngineInventory`, `fetchWebAnalytics`, `formErrorMessages`, `get`, `increment`, `isCraftNative`, `isDark`, `isLoginResponse`, `isTwoFactorChallenge`, `mapDashboardInboxEntry`, `normalizeDeployment`, `normalizeDeploymentDetail`, `normalizeDeploymentList`, `normalizeInboxPreview`, `openTerminalSession`, `pageControls`, `parseDashboardDate`, `parseInboxSender`, `patch`, `planRecoveryRestore`, `post`, `preferredDark`, `previewDeploymentRollback`, `protectRecoveryPoint`, `publicApplicationUrl`, `pushToast`, `put`, `reconcileMigrationLedger`, `recordAnalyticsEvent`, `registerRemoteTerminal`, `requestRemoteCommand`, `reset`, `resizeTerminalSession`, `resolveApiBaseUrl`, `retryFailedJobs`, `retryJob`, `retryNotificationDelivery`, `retryQueueFailures`, `runDeploymentRollback`, `runRecoveryPolicy`, `runRecoveryRetention`, `runSchedulerTask`, `safeAuthRedirect`, `saveBlogPost`, `saveMailPreference`, `sendInboxEmail`, `sendTerminalInput`, `setInboxEmailReadState`, `slugify`, `summarizeDeployments`, `testRecoveryDestination`, `toggleDark`, `updateDeployScript`, `updateIncident`, `updateSchedulerTask`, `useAnalytics`, `useApi`, `useAppControls`, `useAuth`, `useBillable`, `useCategories`, `useCategorizables`, `useCoupons`, `useCouriers`, `useCraft`, `useCraftSidebarSelection`, `useCustomers`, `useDashboard`, `useDeliveryRoutes`, `useDigitalDeliveries`, `useDirectUpload`, `useErrors`, `useGiftCards`, `useInbox`, `useJobs`, `useLicenseKeys`, `useManufacturers`, `useOrders`, `usePages`, `usePayments`, `usePosts`, `useProducts`, `useQueue`, `useRealtimeStats`, `useRecordsDashboard`, `useReleases`, `useReviews`, `useShippingMethods`, `useShippingRates`, `useShippingZones`, `useTaggables`, `useTaxRates`, `useToasts`, `useUnits`, `useVariants`, `useWaitlistProducts`, `useWaitlistRestaurants`, `useWebsockets`, `useWindowControls`, `verifyRecoveryPoint` |
43+
| `auto-imports/functions.ts` | `DashboardApiEnvironmentError`, `DashboardApiError`, `LARGEST_PAGE`, `applyMigrationOperations`, `askBuddy`, `authGuard`, `badgeClassToggles`, `badgeClasses`, `badgeDotClassToggles`, `badgeDotClasses`, `badgeShowsDot`, `buttonVariantClasses`, `buttonVariantToggles`, `clearAllToasts`, `clearBuddyChat`, `closeTerminalSession`, `count`, `createDashboardResource`, `createRecoveryDestination`, `createRecoveryPolicy`, `createResource`, `dashboardApi`, `dashboardDownload`, `dashboardStream`, `decideRelease`, `decrement`, `del`, `deleteBlogPost`, `deleteInboxEmail`, `describeAuthError`, `dismissToast`, `downloadDashboardInboxAttachment`, `ensureCraftNativeSidebarMarker`, `errorGroupPageParams`, `errorGroupParams`, `fetchAuditOperations`, `fetchBillingOverview`, `fetchBlogPost`, `fetchBlogPosts`, `fetchBuddyChat`, `fetchCampaignAnalytics`, `fetchCapturedMail`, `fetchCapturedMailMessage`, `fetchChangeOperations`, `fetchDashboardData`, `fetchDashboardHome`, `fetchDashboardInbox`, `fetchDashboardInboxBody`, `fetchDashboardQueries`, `fetchDashboardQuery`, `fetchDeployScript`, `fetchDeployment`, `fetchDeploymentTerminal`, `fetchDeployments`, `fetchEventAnalytics`, `fetchGlobalSearch`, `fetchInboxActivity`, `fetchIncidentOperations`, `fetchJob`, `fetchJobDefinitions`, `fetchJobStats`, `fetchJobs`, `fetchMailPreference`, `fetchMigrationOperations`, `fetchNotificationDeliveries`, `fetchNotificationDeliveryHistory`, `fetchNotificationDeliveryOverview`, `fetchQueueStats`, `fetchQueueWorkers`, `fetchRealtimeDashboard`, `fetchRecoveryOperations`, `fetchRemoteRegistry`, `fetchSalesAnalytics`, `fetchSchedulerOperations`, `fetchSearchEngineInventory`, `fetchWebAnalytics`, `formErrorMessages`, `get`, `increment`, `isCraftNative`, `isDark`, `isLoginResponse`, `isTwoFactorChallenge`, `mapDashboardInboxEntry`, `normalizeDeployment`, `normalizeDeploymentDetail`, `normalizeDeploymentList`, `normalizeInboxPreview`, `openTerminalSession`, `pageControls`, `parseDashboardDate`, `parseInboxSender`, `patch`, `planRecoveryRestore`, `post`, `preferredDark`, `previewDeploymentRollback`, `protectRecoveryPoint`, `publicApplicationUrl`, `pushToast`, `put`, `readEveryPage`, `reconcileMigrationLedger`, `recordAnalyticsEvent`, `registerRemoteTerminal`, `requestRemoteCommand`, `reset`, `resizeTerminalSession`, `resolveApiBaseUrl`, `retryFailedJobs`, `retryJob`, `retryNotificationDelivery`, `retryQueueFailures`, `runDeploymentRollback`, `runRecoveryPolicy`, `runRecoveryRetention`, `runSchedulerTask`, `safeAuthRedirect`, `saveBlogPost`, `saveMailPreference`, `sendInboxEmail`, `sendTerminalInput`, `setInboxEmailReadState`, `slugify`, `summarizeDeployments`, `testRecoveryDestination`, `toggleDark`, `updateDeployScript`, `updateIncident`, `updateSchedulerTask`, `useAnalytics`, `useApi`, `useAppControls`, `useAuth`, `useBillable`, `useCategories`, `useCategorizables`, `useCoupons`, `useCouriers`, `useCraft`, `useCraftSidebarSelection`, `useCustomers`, `useDashboard`, `useDeliveryRoutes`, `useDigitalDeliveries`, `useDirectUpload`, `useErrors`, `useGiftCards`, `useInbox`, `useJobs`, `useLicenseKeys`, `useManufacturers`, `useOrders`, `usePages`, `usePayments`, `usePosts`, `useProducts`, `useQueue`, `useRealtimeStats`, `useRecordsDashboard`, `useReleases`, `useReviews`, `useServerList`, `useShippingMethods`, `useShippingRates`, `useShippingZones`, `useTaggables`, `useTaxRates`, `useToasts`, `useUnits`, `useVariants`, `useWaitlistProducts`, `useWaitlistRestaurants`, `useWebsockets`, `useWindowControls`, `verifyRecoveryPoint` |
4444
| `auto-imports/models.ts` + ORM proxies | `Activity`, `AnalyticsEvent`, `Auction`, `AuctionItem`, `Author`, `Automation`, `AutomationRun`, `Bid`, `Board`, `BoardColumn`, `Campaign`, `CampaignSend`, `CampaignVariant`, `Card`, `CardAttachment`, `CardComment`, `Cart`, `CartItem`, `Categorizable`, `Category`, `Comment`, `CommunicationSuppression`, `ConsentEvent`, `Coupon`, `Courier`, `CourierPing`, `Customer`, `DeliveryRoute`, `DeliveryStop`, `Deployment`, `DigitalDelivery`, `EmailIdempotency`, `EmailList`, `EmailListSubscriber`, `EmailSuppression`, `EmailWebhookEvent`, `ErrorModel`, `FailedJob`, `FeedbackToken`, `Form`, `FormField`, `FormSubmission`, `GdprRequest`, `GiftCard`, `Job`, `Label`, `LicenseKey`, `Log`, `LoyaltyPoint`, `LoyaltyReward`, `MagicLinkToken`, `MailPreference`, `Manufacturer`, `Menu`, `MenuItem`, `Notification`, `NotificationDelivery`, `Order`, `OrderIdempotency`, `OrderItem`, `Page`, `PageRevision`, `Payment`, `PaymentMethod`, `PaymentProduct`, `PaymentTransaction`, `PaymentWebhookEvent`, `PersonalAccessToken`, `Pledge`, `Post`, `PrintDevice`, `Product`, `ProductUnit`, `ProductVariant`, `QueryLog`, `QueueWorker`, `Receipt`, `Redirect`, `Referral`, `ReferralCode`, `Release`, `Review`, `SenderDomain`, `ShippingMethod`, `ShippingRate`, `ShippingZone`, `Site`, `SiteDomain`, `SmsOptOut`, `SocialAccount`, `SocialPost`, `StorageItem`, `StorageItemTask`, `Subscriber`, `SubscriberEmail`, `Subscription`, `Tag`, `TaxRate`, `Team`, `TeamInvitation`, `TeamMember`, `Transaction`, `UsageEvent`, `User`, `WaitlistProduct`, `WaitlistRestaurant`, `Websocket` |
4545
| `auto-imports/jobs.ts` | `ExampleJob`, `ExampleTwoJob`, `GenerateStoragePreviewJob`, `Inspire`, `OptimizeStorageImageJob`, `PruneModelsJob`, `PruneQueryLogsJob`, `PruneRetainedDataJob`, `PublishScheduledPagesJob`, `SendCampaignJob`, `SendEmailJob`, `SendPasswordResetEmailJob`, `SendWelcomeEmail`, `SyncSearchIndexJob`, `TagStorageMediaJob`, `TranscodeStorageVideoJob` |
4646
| `auto-imports/controllers.ts` | `ComingSoonController`, `QueryController` |

‎storage/framework/auto-imports/actions.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ export const actions = {
331331
'Actions/Dashboard/Commerce/commerce-customer-records': '../defaults/app/Actions/Dashboard/Commerce/commerce-customer-records.ts',
332332
'Actions/Dashboard/Commerce/commerce-dashboard': '../defaults/app/Actions/Dashboard/Commerce/commerce-dashboard.ts',
333333
'Actions/Dashboard/Commerce/commerce-delivery-overview': '../defaults/app/Actions/Dashboard/Commerce/commerce-delivery-overview.ts',
334+
'Actions/Dashboard/Commerce/commerce-delivery-route-list': '../defaults/app/Actions/Dashboard/Commerce/commerce-delivery-route-list.ts',
334335
'Actions/Dashboard/Commerce/commerce-delivery': '../defaults/app/Actions/Dashboard/Commerce/commerce-delivery.ts',
335336
'Actions/Dashboard/Commerce/commerce-gift-card-list': '../defaults/app/Actions/Dashboard/Commerce/commerce-gift-card-list.ts',
336337
'Actions/Dashboard/Commerce/commerce-order-list': '../defaults/app/Actions/Dashboard/Commerce/commerce-order-list.ts',

‎storage/framework/auto-imports/functions.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ export type { BadgeVariant, BadgeSize } from '../defaults/functions/dashboard-ba
5353
export { buttonVariantToggles, buttonVariantClasses } from '../defaults/functions/dashboard-button'
5454
export { parseDashboardDate } from '../defaults/functions/dashboard-dates'
5555
export { formErrorMessages, slugify } from '../defaults/functions/dashboard-form'
56-
export { pageControls, useRecordsDashboard } from '../defaults/functions/dashboard-list'
57-
export type { PageControls, RecordsDashboardOptions, RecordsDashboard } from '../defaults/functions/dashboard-list'
56+
export { readEveryPage, pageControls, useRecordsDashboard, useServerList, LARGEST_PAGE } from '../defaults/functions/dashboard-list'
57+
export type { PageControls, RecordsDashboardOptions, RecordsDashboard, ServerListPage, ServerListOptions, ServerList } from '../defaults/functions/dashboard-list'
5858
export { fetchDashboardHome, useDashboard } from '../defaults/functions/dashboard'
5959
export type { DashboardStats, ActivityItem, SystemHealthItem, DashboardHttpMetric, DashboardIssue } from '../defaults/functions/dashboard'
6060
export { fetchDashboardData } from '../defaults/functions/data'
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"fingerprint": "5ec873edaed57eb99997d16e797df2ab4ac3ae8587353e913995ae5fa79a268c"
2+
"fingerprint": "4f2c0a6599ac49fcec8d88e6c39bf2a34ed18ac4f7e6048f05df7d509abf7e94"
33
}

‎storage/framework/defaults/app/Actions/Dashboard/Commerce/DeliveryRouteIndexAction.ts‎

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,35 @@
1+
import type { RequestInstance } from '@stacksjs/types'
12
import { Action } from '@stacksjs/actions/runtime'
2-
import { DeliveryRoute, Courier } from '@stacksjs/orm'
3+
import { response } from '@stacksjs/router'
34
import { dashboardOperationalError } from '../dashboard-response'
4-
import {
5-
indexDeliveryRouteCouriers,
6-
normalizeDeliveryRouteRecord,
7-
} from './delivery-route-records'
5+
import { parseDeliveryRouteListQuery, readDeliveryRoutePage } from './commerce-delivery-route-list'
86

7+
/**
8+
* `GET /api/dashboard/commerce/delivery-routes` - one page of delivery routes,
9+
* searched, sorted and counted by the database, with the summary of every
10+
* route (commerce-delivery-route-list.ts, stacksjs/stacks#3218). It used to
11+
* return the 500 most recently active routes as a bare array.
12+
*/
913
export default new Action({
1014
name: 'Dashboard Delivery Routes',
11-
description: 'Returns validated delivery routes and their optional Courier relationships.',
15+
description: 'Returns one page of delivery routes with their linked couriers.',
1216
method: 'GET',
1317
apiResponse: true,
1418

15-
async handle() {
19+
async handle(request: RequestInstance) {
20+
const query = parseDeliveryRouteListQuery(key => request.get<unknown>(key, ''))
21+
if ('error' in query)
22+
return response.json({ message: query.error }, 422)
23+
1624
try {
17-
const routes = await DeliveryRoute.orderByDesc('last_active').limit(500).get()
18-
const courierIds = [...new Set(routes.map(route => route.get('courier_id')).filter(Boolean))]
19-
const couriers = courierIds.length ? await Courier.whereIn('id', courierIds).get() : []
20-
const couriersById = indexDeliveryRouteCouriers(couriers)
21-
return routes.map(route => normalizeDeliveryRouteRecord(route, couriersById))
25+
const routes = await readDeliveryRoutePage(query)
26+
return {
27+
records: routes.records,
28+
total: routes.total,
29+
page: routes.page,
30+
perPage: query.perPage,
31+
summary: routes.summary,
32+
}
2233
}
2334
catch (error) {
2435
return dashboardOperationalError(error, 'Delivery route records could not be read.', 'DeliveryRouteIndexAction')

‎storage/framework/defaults/app/Actions/Dashboard/Commerce/commerce-delivery-overview.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ function activeFirst(dialect: QueryBuilderDialect): string {
3535
* else is kept from the day before the cutoff on, and buildDeliveryOverview
3636
* makes the exact cut.
3737
*/
38-
function recentRoutes(dialect: QueryBuilderDialect, now: Date): { where: string, values: unknown[] } {
38+
export function recentRoutes(dialect: QueryBuilderDialect, now: Date): { where: string, values: unknown[] } {
3939
const { bind, values } = binder(dialect)
4040
const cutoff = now.getTime() - DAY_MS
4141
const column = asText('last_active', dialect)

0 commit comments

Comments
 (0)