@aglyn/plugins-commerce 1.0.0-beta.232 → 1.0.0-beta.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/package.json +13 -13
- package/src/lib/components/cart-extras.d.ts +6 -0
- package/src/lib/components/cart-extras.js +4 -1
- package/src/lib/components/cart-extras.js.map +1 -1
- package/src/lib/components/cart-fulfillment.d.ts +117 -0
- package/src/lib/components/cart-fulfillment.js +367 -0
- package/src/lib/components/cart-fulfillment.js.map +1 -0
- package/src/lib/components/cart.js +52 -10
- package/src/lib/components/cart.js.map +1 -1
- package/src/lib/components/commerce-console-page.js +42 -1
- package/src/lib/components/commerce-console-page.js.map +1 -1
- package/src/lib/components/console/host-orders-card.component.js +38 -10
- package/src/lib/components/console/host-orders-card.component.js.map +1 -1
- package/src/lib/components/console/local-delivery-settings-card.component.d.ts +37 -0
- package/src/lib/components/console/local-delivery-settings-card.component.js +542 -0
- package/src/lib/components/console/local-delivery-settings-card.component.js.map +1 -0
- package/src/lib/components/console/locations-card.component.js +177 -3
- package/src/lib/components/console/locations-card.component.js.map +1 -1
- package/src/lib/components/console/order-detail-dialog.component.js +10 -0
- package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
- package/src/lib/components/console/order-local-fulfillment-panel.component.d.ts +44 -0
- package/src/lib/components/console/order-local-fulfillment-panel.component.js +265 -0
- package/src/lib/components/console/order-local-fulfillment-panel.component.js.map +1 -0
- package/src/lib/components/console/order-offline-notice.component.d.ts +12 -0
- package/src/lib/components/console/order-offline-notice.component.js +53 -0
- package/src/lib/components/console/order-offline-notice.component.js.map +1 -0
- package/src/lib/components/console/pickup-delivery-queue-card.component.d.ts +35 -0
- package/src/lib/components/console/pickup-delivery-queue-card.component.js +222 -0
- package/src/lib/components/console/pickup-delivery-queue-card.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-api.d.ts +24 -1
- package/src/lib/components/console/pos/pos-api.js +13 -0
- package/src/lib/components/console/pos/pos-api.js.map +1 -1
- package/src/lib/components/console/pos/pos-item-dialog.component.d.ts +12 -0
- package/src/lib/components/console/pos/pos-item-dialog.component.js +10 -8
- package/src/lib/components/console/pos/pos-item-dialog.component.js.map +1 -1
- package/src/lib/components/console/pos/pos-kiosk-queue.component.d.ts +22 -0
- package/src/lib/components/console/pos/pos-kiosk-queue.component.js +168 -0
- package/src/lib/components/console/pos/pos-kiosk-queue.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-tender-dialogs.component.d.ts +18 -0
- package/src/lib/components/console/pos/pos-tender-dialogs.component.js +116 -1
- package/src/lib/components/console/pos/pos-tender-dialogs.component.js.map +1 -1
- package/src/lib/components/console/pos/pos-tender-panel.component.d.ts +8 -0
- package/src/lib/components/console/pos/pos-tender-panel.component.js +77 -18
- package/src/lib/components/console/pos/pos-tender-panel.component.js.map +1 -1
- package/src/lib/components/console/pos/use-pos-display.d.ts +5 -2
- package/src/lib/components/console/pos/use-pos-display.js +3 -2
- package/src/lib/components/console/pos/use-pos-display.js.map +1 -1
- package/src/lib/components/console/pos-devices-card.component.js +2 -2
- package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
- package/src/lib/components/console/pos-display/pairing-screen.d.ts +5 -2
- package/src/lib/components/console/pos-display/pairing-screen.js +7 -5
- package/src/lib/components/console/pos-display/pairing-screen.js.map +1 -1
- package/src/lib/components/console/pos-display/pos-display-api.d.ts +7 -1
- package/src/lib/components/console/pos-display/pos-display-api.js +7 -2
- package/src/lib/components/console/pos-display/pos-display-api.js.map +1 -1
- package/src/lib/components/console/pos-kiosk/kiosk-checkout.d.ts +41 -0
- package/src/lib/components/console/pos-kiosk/kiosk-checkout.js +275 -0
- package/src/lib/components/console/pos-kiosk/kiosk-checkout.js.map +1 -0
- package/src/lib/components/console/pos-kiosk/kiosk-menu.d.ts +32 -0
- package/src/lib/components/console/pos-kiosk/kiosk-menu.js +344 -0
- package/src/lib/components/console/pos-kiosk/kiosk-menu.js.map +1 -0
- package/src/lib/components/console/pos-kiosk/kiosk-staff.d.ts +12 -0
- package/src/lib/components/console/pos-kiosk/kiosk-staff.js +257 -0
- package/src/lib/components/console/pos-kiosk/kiosk-staff.js.map +1 -0
- package/src/lib/components/console/pos-kiosk/pos-kiosk-api.d.ts +114 -0
- package/src/lib/components/console/pos-kiosk/pos-kiosk-api.js +189 -0
- package/src/lib/components/console/pos-kiosk/pos-kiosk-api.js.map +1 -0
- package/src/lib/components/console/pos-kiosk/use-kiosk-idle.d.ts +16 -0
- package/src/lib/components/console/pos-kiosk/use-kiosk-idle.js +80 -0
- package/src/lib/components/console/pos-kiosk/use-kiosk-idle.js.map +1 -0
- package/src/lib/components/console/pos-kiosk-page.component.d.ts +28 -0
- package/src/lib/components/console/pos-kiosk-page.component.js +791 -0
- package/src/lib/components/console/pos-kiosk-page.component.js.map +1 -0
- package/src/lib/components/console/pos-offline/pos-offline-api.d.ts +44 -0
- package/src/lib/components/console/pos-offline/pos-offline-api.js +69 -0
- package/src/lib/components/console/pos-offline/pos-offline-api.js.map +1 -0
- package/src/lib/components/console/pos-offline/pos-offline-banner.component.d.ts +7 -0
- package/src/lib/components/console/pos-offline/pos-offline-banner.component.js +135 -0
- package/src/lib/components/console/pos-offline/pos-offline-banner.component.js.map +1 -0
- package/src/lib/components/console/pos-offline/pos-offline-checkout.component.d.ts +32 -0
- package/src/lib/components/console/pos-offline/pos-offline-checkout.component.js +287 -0
- package/src/lib/components/console/pos-offline/pos-offline-checkout.component.js.map +1 -0
- package/src/lib/components/console/pos-offline/pos-offline-store.d.ts +63 -0
- package/src/lib/components/console/pos-offline/pos-offline-store.js +145 -0
- package/src/lib/components/console/pos-offline/pos-offline-store.js.map +1 -0
- package/src/lib/components/console/pos-offline/register-offline.d.ts +24 -0
- package/src/lib/components/console/pos-offline/register-offline.js +24 -0
- package/src/lib/components/console/pos-offline/register-offline.js.map +1 -0
- package/src/lib/components/console/pos-offline/use-pos-offline.d.ts +85 -0
- package/src/lib/components/console/pos-offline/use-pos-offline.js +542 -0
- package/src/lib/components/console/pos-offline/use-pos-offline.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-shift-history-card.component.js +72 -17
- package/src/lib/components/console/pos-ops/pos-shift-history-card.component.js.map +1 -1
- package/src/lib/components/console/pos-page.component.js +174 -27
- package/src/lib/components/console/pos-page.component.js.map +1 -1
- package/src/lib/components/console/products-hub-card.component.js +35 -10
- package/src/lib/components/console/products-hub-card.component.js.map +1 -1
- package/src/lib/components/console/shipping-settings-card.component.js +1 -1
- package/src/lib/components/console/shipping-settings-card.component.js.map +1 -1
- package/src/lib/components/console/stock-movements-card.component.js +48 -16
- package/src/lib/components/console/stock-movements-card.component.js.map +1 -1
- package/src/lib/components/console/store-zones.d.ts +8 -1
- package/src/lib/components/console/store-zones.js +11 -1
- package/src/lib/components/console/store-zones.js.map +1 -1
- package/src/lib/components/order-status.js +66 -0
- package/src/lib/components/order-status.js.map +1 -1
- package/src/lib/components/product-detail.js +42 -15
- package/src/lib/components/product-detail.js.map +1 -1
- package/src/lib/constants/local-fulfillment-list-query.d.ts +48 -0
- package/src/lib/constants/local-fulfillment-list-query.js +84 -0
- package/src/lib/constants/local-fulfillment-list-query.js.map +1 -0
- package/src/lib/constants/orders-list-query.d.ts +7 -2
- package/src/lib/constants/orders-list-query.js +89 -8
- package/src/lib/constants/orders-list-query.js.map +1 -1
- package/src/lib/constants/pos-shift-list-query.d.ts +22 -0
- package/src/lib/constants/pos-shift-list-query.js +115 -0
- package/src/lib/constants/pos-shift-list-query.js.map +1 -0
- package/src/lib/constants/product-list-query.d.ts +6 -1
- package/src/lib/constants/product-list-query.js +77 -8
- package/src/lib/constants/product-list-query.js.map +1 -1
- package/src/lib/constants/stock-movement-query.d.ts +3 -2
- package/src/lib/constants/stock-movement-query.js +52 -8
- package/src/lib/constants/stock-movement-query.js.map +1 -1
- package/src/lib/model/buyer-notifications.d.ts +5 -3
- package/src/lib/model/buyer-notifications.js +25 -4
- package/src/lib/model/buyer-notifications.js.map +1 -1
- package/src/lib/model/commerce-orders.d.ts +59 -0
- package/src/lib/model/commerce-orders.js.map +1 -1
- package/src/lib/model/commerce-pos-kiosk.d.ts +175 -0
- package/src/lib/model/commerce-pos-kiosk.js +147 -0
- package/src/lib/model/commerce-pos-kiosk.js.map +1 -0
- package/src/lib/model/commerce-pos-offline.d.ts +234 -0
- package/src/lib/model/commerce-pos-offline.js +301 -0
- package/src/lib/model/commerce-pos-offline.js.map +1 -0
- package/src/lib/model/commerce-pos-ops.d.ts +16 -6
- package/src/lib/model/commerce-pos-ops.js +12 -4
- package/src/lib/model/commerce-pos-ops.js.map +1 -1
- package/src/lib/model/commerce-pos.d.ts +13 -1
- package/src/lib/model/commerce-pos.js +5 -4
- package/src/lib/model/commerce-pos.js.map +1 -1
- package/src/lib/model/commerce-stock-holds.d.ts +24 -3
- package/src/lib/model/commerce-stock-holds.js +26 -8
- package/src/lib/model/commerce-stock-holds.js.map +1 -1
- package/src/lib/model/commerce.d.ts +3 -0
- package/src/lib/model/commerce.js.map +1 -1
- package/src/lib/model/index.d.ts +4 -0
- package/src/lib/model/index.js +4 -0
- package/src/lib/model/index.js.map +1 -1
- package/src/lib/model/local-fulfillment-settings.d.ts +178 -0
- package/src/lib/model/local-fulfillment-settings.js +361 -0
- package/src/lib/model/local-fulfillment-settings.js.map +1 -0
- package/src/lib/model/order-list-fields.d.ts +10 -1
- package/src/lib/model/order-list-fields.js +1 -0
- package/src/lib/model/order-list-fields.js.map +1 -1
- package/src/lib/model/order-local-fulfillment.d.ts +131 -0
- package/src/lib/model/order-local-fulfillment.js +125 -0
- package/src/lib/model/order-local-fulfillment.js.map +1 -0
- package/src/lib/model/order-shipping-export.js +4 -0
- package/src/lib/model/order-shipping-export.js.map +1 -1
- package/src/lib/model/order-status-view.d.ts +20 -1
- package/src/lib/model/order-status-view.js +60 -6
- package/src/lib/model/order-status-view.js.map +1 -1
- package/src/lib/plugin-config.js +6 -2
- package/src/lib/plugin-config.js.map +1 -1
- package/src/lib/plugin.js +8 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/pos-kiosk-config.d.ts +26 -0
- package/src/lib/pos-kiosk-config.js +59 -0
- package/src/lib/pos-kiosk-config.js.map +1 -0
- package/src/lib/server/api-v1/openapi.js +8 -0
- package/src/lib/server/api-v1/openapi.js.map +1 -1
- package/src/lib/server/api-v1/order-view.d.ts +19 -0
- package/src/lib/server/api-v1/order-view.js +35 -0
- package/src/lib/server/api-v1/order-view.js.map +1 -1
- package/src/lib/server/billing-webhook.js +238 -87
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/cart-checkout.d.ts +2 -0
- package/src/lib/server/cart-checkout.js +173 -13
- package/src/lib/server/cart-checkout.js.map +1 -1
- package/src/lib/server/checkout-extras.js +18 -4
- package/src/lib/server/checkout-extras.js.map +1 -1
- package/src/lib/server/checkout.js +71 -14
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/local-fulfillment-status.d.ts +86 -0
- package/src/lib/server/local-fulfillment-status.js +379 -0
- package/src/lib/server/local-fulfillment-status.js.map +1 -0
- package/src/lib/server/local-fulfillment.d.ts +190 -0
- package/src/lib/server/local-fulfillment.js +580 -0
- package/src/lib/server/local-fulfillment.js.map +1 -0
- package/src/lib/server/membership-register.js +3 -1
- package/src/lib/server/membership-register.js.map +1 -1
- package/src/lib/server/order-notifications.js +61 -0
- package/src/lib/server/order-notifications.js.map +1 -1
- package/src/lib/server/pos-display.d.ts +27 -1
- package/src/lib/server/pos-display.js +50 -9
- package/src/lib/server/pos-display.js.map +1 -1
- package/src/lib/server/pos-kiosk-principal.d.ts +29 -0
- package/src/lib/server/pos-kiosk-principal.js +42 -0
- package/src/lib/server/pos-kiosk-principal.js.map +1 -0
- package/src/lib/server/pos-kiosk.d.ts +54 -0
- package/src/lib/server/pos-kiosk.js +972 -0
- package/src/lib/server/pos-kiosk.js.map +1 -0
- package/src/lib/server/pos-offline-sync.d.ts +85 -0
- package/src/lib/server/pos-offline-sync.js +765 -0
- package/src/lib/server/pos-offline-sync.js.map +1 -0
- package/src/lib/server/pos-order.js +22 -4
- package/src/lib/server/pos-order.js.map +1 -1
- package/src/lib/server/pos-payment.d.ts +6 -0
- package/src/lib/server/pos-payment.js +225 -6
- package/src/lib/server/pos-payment.js.map +1 -1
- package/src/lib/server/pos-print.js +5 -0
- package/src/lib/server/pos-print.js.map +1 -1
- package/src/lib/server/pos-return.js +27 -0
- package/src/lib/server/pos-return.js.map +1 -1
- package/src/lib/server/pos-sale.d.ts +6 -0
- package/src/lib/server/pos-sale.js +2 -0
- package/src/lib/server/pos-sale.js.map +1 -1
- package/src/lib/server/pos-shift.js +8 -1
- package/src/lib/server/pos-shift.js.map +1 -1
- package/src/lib/server/refund.js +3 -0
- package/src/lib/server/refund.js.map +1 -1
- package/src/lib/server/stock-hold.d.ts +9 -1
- package/src/lib/server/stock-hold.js +15 -7
- package/src/lib/server/stock-hold.js.map +1 -1
- package/src/lib/server.js +16 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/tenant-emails.js +161 -0
- package/src/lib/tenant-emails.js.map +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../libs/plugins/commerce/src/lib/components/console/pos-offline/use-pos-offline.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport * as CommerceModel from '../../../model'\nimport {\n posOfflineChangeCents,\n posOfflineSaleTotals,\n POS_OFFLINE_SYNC_BATCH_MAX,\n type PosOfflineFlag,\n type PosOfflineKit,\n type PosOfflineSale,\n type PosOfflineSaleLine,\n type PosOfflineStockConflict,\n} from '../../../model/commerce-pos-offline'\nimport type { RegisterLine } from '../pos/pos-cart-panel.component'\nimport { fetchPosOfflineKit, newPosOfflineSaleKey, syncPosOfflineSales } from './pos-offline-api'\nimport {\n openPosOfflineStore,\n posOfflineProduct,\n type PosOfflineProduct,\n type PosOfflineQueuedSale,\n type PosOfflineStore,\n} from './pos-offline-store'\n\n/*==========================================\n * THE REGISTER, OFFLINE (AGL-3625).\n *\n * WHEN IS IT OFFLINE: when the browser says so, or when a register call\n * failed at the network — a Wi-Fi that is \"connected\" to a router with no\n * uplink looks online to `navigator.onLine`. While offline it probes the sync\n * route every few seconds; the first answer brings it back.\n *\n * WHAT IT KEEPS: the kit and the catalog, refreshed while online, and the\n * queue (see `pos-offline-store.ts`).\n *\n * HOW IT SYNCS: on reconnect, on mount, and after every offline sale while\n * online again, in batches, one sync at a time. A sale leaves the device only\n * when the server has answered for it — recorded, or a replay of a recording —\n * so a connection that drops mid-request leaves it queued, and sending it\n * again is safe: the server lands a sale key once.\n *=========================================*/\n\n/** How often the kit is refreshed while online. */\nconst KIT_REFRESH_MS = 10 * 60 * 1000\n/** How often the whole catalog is re-read for the device. */\nconst CATALOG_REFRESH_MS = 30 * 60 * 1000\n/** How often an offline register asks whether the server answers again. */\nconst PROBE_MS = 15 * 1000\n\ntype User = Parameters<typeof fetchPosOfflineKit>[0]\n\n/** A sale the server answered for with something to check. */\nexport interface PosOfflineSyncNotice {\n saleKey: string\n orderId: string\n number: number | null\n flags: PosOfflineFlag[]\n stockConflicts: PosOfflineStockConflict[]\n}\n\n/** A cash sale the register just rang offline, for its receipt step. */\nexport interface PosOfflineRungSale {\n saleKey: string\n sale: PosOfflineSale\n /** The order the receipt prints, shaped as the stored order will be. */\n order: CommerceModel.HostOrder & { createdAtMs: number; customerName?: string | null }\n}\n\nexport interface PosOfflineState {\n /** The register is offline right now. */\n offline: boolean\n /** This device can sell offline: storage opened and the kit says yes. */\n ready: boolean\n /** Why it cannot, in the cashier's words, when it cannot. */\n unavailableReason: string | null\n kit: PosOfflineKit | null\n /** Sales on this device that have not synced, oldest first. */\n queue: PosOfflineQueuedSale[]\n syncing: boolean\n /** Why the last sync could not run, when the server refused it outright. */\n syncError: string | null\n /** Synced sales the server flagged, until the cashier dismisses them. */\n notices: PosOfflineSyncNotice[]\n /** The products the offline grid shows for a search and a chip. */\n gridProducts(search: string, categoryId: string, quickKeysId: string): PosOfflineProduct[]\n /** A product and variant by barcode or SKU from the cached catalog. */\n findByCode(code: string): { product: PosOfflineProduct; variant: any } | null\n /** The totals the basket rings offline, or `null` when it cannot. */\n totalsFor(lines: RegisterLine[], discountPct: number): CommerceModel.OrderTotals | null\n /** Queues one cash sale; `null` (and nothing taken) when it cannot be kept. */\n ringCashSale(input: {\n lines: RegisterLine[]\n discountPct: number\n cashTenderedCents: number\n customer?: { email?: string | null; name?: string | null; kind?: string; id?: string } | null\n }): Promise<PosOfflineRungSale | null>\n /** A register call failed at the network. */\n reportNetworkFailure(): void\n syncNow(): Promise<void>\n dismissNotices(): void\n /** Removes a refused sale the server will never take, after the cashier confirms. */\n discard(saleKey: string): Promise<void>\n}\n\nexport interface UsePosOfflineInput {\n hostId: string\n user: User | null | undefined\n registerId: string\n /** The open shift on the register as last seen, if any. */\n openShiftId?: string | null\n locationId?: string\n cashierAssertion?: string\n /** Products the live grid holds; kept for the device as they arrive. */\n liveProducts: ReadonlyArray<Record<string, any>>\n /** Reads the whole sellable catalog for the device (the grid's own query). */\n loadCatalog?: () => Promise<Array<Record<string, any>>>\n /** Opens the device store; injected by specs. */\n openStore?: typeof openPosOfflineStore\n now?: () => number\n}\n\n/**\n * The shift open on the register as last seen: the live register document's\n * answer when the page has one (including \"none\"), the kit's otherwise.\n */\nfunction lastKnownShift(live: string | null | undefined, kit: PosOfflineKit, registerId: string): string | null {\n if (live !== undefined) return live || null\n return kit.registers.find((register) => register.id === registerId)?.openShiftId ?? null\n}\n\nfunction browserOnline(): boolean {\n return typeof navigator === 'undefined' || navigator.onLine !== false\n}\n\nexport function usePosOffline(input: UsePosOfflineInput): PosOfflineState {\n const { hostId, user, registerId } = input\n const uid = (user as { uid?: string } | null | undefined)?.uid ?? ''\n const now = input.now ?? Date.now\n const openStore = input.openStore ?? openPosOfflineStore\n\n const [store, setStore] = useState<PosOfflineStore | null>(null)\n const [storeFailed, setStoreFailed] = useState(false)\n const [kit, setKit] = useState<PosOfflineKit | null>(null)\n const [catalog, setCatalog] = useState<PosOfflineProduct[]>([])\n const [queue, setQueue] = useState<PosOfflineQueuedSale[]>([])\n const [navigatorOnline, setNavigatorOnline] = useState(browserOnline)\n const [networkFailed, setNetworkFailed] = useState(false)\n const [syncing, setSyncing] = useState(false)\n const [syncError, setSyncError] = useState<string | null>(null)\n const [notices, setNotices] = useState<PosOfflineSyncNotice[]>([])\n const offline = !navigatorOnline || networkFailed\n\n const latest = useRef(input)\n latest.current = input\n const userRef = useRef(user)\n userRef.current = user\n\n // The device store, for this member at this site.\n useEffect(() => {\n let active = true\n setStore(null)\n setStoreFailed(false)\n setKit(null)\n setCatalog([])\n setQueue([])\n if (!uid || !hostId) return undefined\n void openStore({ uid, hostId }).then(async (opened) => {\n if (!active) return\n if (!opened) {\n setStoreFailed(true)\n return\n }\n try {\n const [saved, sales] = await Promise.all([opened.loadKit(), opened.listSales()])\n if (!active) return\n if (saved?.kit) setKit(saved.kit)\n if (saved?.products?.length) setCatalog(saved.products)\n setQueue(sales)\n setStore(opened)\n } catch {\n if (active) setStoreFailed(true)\n }\n })\n return () => {\n active = false\n }\n }, [uid, hostId, openStore])\n\n useEffect(() => {\n const goOnline = () => {\n setNavigatorOnline(true)\n setNetworkFailed(false)\n }\n const goOffline = () => setNavigatorOnline(false)\n window.addEventListener('online', goOnline)\n window.addEventListener('offline', goOffline)\n return () => {\n window.removeEventListener('online', goOnline)\n window.removeEventListener('offline', goOffline)\n }\n }, [])\n\n // The kit and the catalog, while online.\n const kitSavedAt = useRef(0)\n const catalogSavedAt = useRef(0)\n const refreshKit = useCallback(async () => {\n const signedIn = userRef.current\n if (!store || !signedIn) return\n const answer = await fetchPosOfflineKit(signedIn, hostId)\n if ('error' in answer) {\n if (answer.status === 0) setNetworkFailed(true)\n return\n }\n const fresh = answer.body.kit\n let products: PosOfflineProduct[] | null = null\n const load = latest.current.loadCatalog\n if (load && now() - catalogSavedAt.current > CATALOG_REFRESH_MS) {\n try {\n products = (await load()).map(posOfflineProduct).filter(Boolean) as PosOfflineProduct[]\n catalogSavedAt.current = now()\n } catch {\n products = null\n }\n }\n kitSavedAt.current = now()\n setKit(fresh)\n if (products) setCatalog(products)\n const saved = await store.loadKit().catch(() => null)\n await store\n .saveKit({\n kit: fresh,\n kitSavedAtMs: kitSavedAt.current,\n products: products ?? saved?.products ?? [],\n catalogSavedAtMs: products ? catalogSavedAt.current : (saved?.catalogSavedAtMs ?? 0),\n })\n .catch(() => undefined)\n }, [store, hostId, now])\n\n useEffect(() => {\n if (!store || offline || !uid) return undefined\n void refreshKit()\n const timer = window.setInterval(() => void refreshKit(), KIT_REFRESH_MS)\n return () => window.clearInterval(timer)\n }, [store, offline, uid, refreshKit])\n\n // Products the live grid shows are merged into the device's catalog, so a\n // product rung up online is sellable offline even past the snapshot.\n const liveKey = input.liveProducts.map((product) => String(product['$id'] ?? '')).join(',')\n useEffect(() => {\n if (!store || offline || !input.liveProducts.length) return\n setCatalog((current) => {\n const byId = new Map(current.map((product) => [product.$id, product]))\n let changed = false\n for (const product of latest.current.liveProducts) {\n const trimmed = posOfflineProduct(product)\n if (!trimmed) continue\n if (JSON.stringify(byId.get(trimmed.$id)) !== JSON.stringify(trimmed)) changed = true\n byId.set(trimmed.$id, trimmed)\n }\n if (!changed) return current\n const next = [...byId.values()]\n void store.loadKit().then((saved) =>\n store.saveKit({\n kit: saved?.kit ?? null,\n kitSavedAtMs: saved?.kitSavedAtMs ?? 0,\n products: next,\n catalogSavedAtMs: saved?.catalogSavedAtMs ?? 0,\n }),\n ).catch(() => undefined)\n return next\n })\n // eslint-disable-next-line react-hooks/exhaustive-deps -- keyed on the ids the grid holds\n }, [store, offline, liveKey])\n\n // The sync.\n const syncInFlight = useRef(false)\n const syncNow = useCallback(async () => {\n const signedIn = userRef.current\n if (!store || !signedIn || syncInFlight.current) return\n syncInFlight.current = true\n setSyncing(true)\n try {\n const all = await store.listSales()\n const waiting = all.filter((entry) => entry.state === 'queued' && entry.sale.signedInUid === uid)\n if (!waiting.length) {\n // Nothing to send: one empty call says whether the route answers.\n if (networkFailed) {\n const probe = await syncPosOfflineSales(signedIn, hostId, [])\n if (probe.ok || probe.status !== 0) setNetworkFailed(false)\n }\n return\n }\n const flagged: PosOfflineSyncNotice[] = []\n for (let at = 0; at < waiting.length; at += POS_OFFLINE_SYNC_BATCH_MAX) {\n const batch = waiting.slice(at, at + POS_OFFLINE_SYNC_BATCH_MAX)\n const answer = await syncPosOfflineSales(\n signedIn,\n hostId,\n batch.map((entry) => entry.sale),\n )\n if ('error' in answer) {\n if (answer.status === 0) setNetworkFailed(true)\n else setSyncError(answer.error)\n break\n }\n setSyncError(null)\n setNetworkFailed(false)\n const byKey = new Map(batch.map((entry) => [entry.saleKey, entry]))\n for (const result of answer.body.results ?? []) {\n const entry = byKey.get(result.saleKey)\n if (!entry) continue\n if (result.status === 'recorded' || result.status === 'replayed') {\n await store.removeSale(entry.saleKey)\n if (result.flags?.length) {\n flagged.push({\n saleKey: result.saleKey,\n orderId: result.orderId ?? result.saleKey,\n number: result.number ?? null,\n flags: result.flags,\n stockConflicts: result.stockConflicts ?? [],\n })\n }\n continue\n }\n // A transient failure stays queued; every other refusal waits for\n // the cashier, still on the device, with the server's sentence.\n await store.putSale({\n ...entry,\n attempts: entry.attempts + 1,\n state: result.reason === 'failed' || result.reason === 'wrong-staff' ? 'queued' : 'refused',\n ...(result.error ? { error: result.error } : {}),\n ...(result.reason ? { reason: result.reason } : {}),\n })\n }\n }\n if (flagged.length) setNotices((current) => [...current, ...flagged])\n } catch {\n // Storage or the network went away mid-sync; the queue is as it was.\n } finally {\n setQueue(await store.listSales().catch(() => []))\n syncInFlight.current = false\n setSyncing(false)\n }\n }, [store, uid, hostId, networkFailed])\n\n // Sync on reconnect, on mount, and while offline-by-failure keep probing.\n useEffect(() => {\n if (!store || offline) return\n void syncNow()\n }, [store, offline, syncNow])\n useEffect(() => {\n if (!store || !networkFailed || !navigatorOnline) return undefined\n const timer = window.setInterval(() => void syncNow(), PROBE_MS)\n return () => window.clearInterval(timer)\n }, [store, networkFailed, navigatorOnline, syncNow])\n\n const unavailableReason = storeFailed\n ? 'This browser is not keeping data for the register (a private window, or storage is blocked), so offline selling is unavailable.'\n : !store\n ? 'Getting the register ready to sell offline…'\n : !kit\n ? 'Connect once so the register can get ready to sell offline.'\n : !kit.available\n ? (kit.unavailableReason ?? 'Offline selling is unavailable for this site.')\n : !catalog.length\n ? 'The register has no catalog saved for offline selling yet.'\n : kit.requireOpenShift && !lastKnownShift(input.openShiftId, kit, registerId)\n ? 'Open a shift before going offline: this site requires one for every sale.'\n : null\n const ready = unavailableReason === null\n\n const gridProducts = useCallback(\n (search: string, categoryId: string, quickKeysId: string) => {\n const typed = search.trim().toLowerCase()\n return catalog\n .filter((product) => product['status'] === undefined || product['status'] === 'active')\n .filter((product) => !product['deletedAt'])\n .filter((product) => {\n // A degraded grid over the device's own copy: there is no query to\n // ask while offline, so the copy is narrowed in memory.\n if (typed) return String(product.name ?? '').toLowerCase().includes(typed)\n if (categoryId === quickKeysId && quickKeysId) return product['posQuickKey'] === true\n if (categoryId) return Array.isArray(product['categoryIds']) && product['categoryIds'].includes(categoryId)\n return true\n })\n .sort((a, b) => String(a.name ?? '').localeCompare(String(b.name ?? '')))\n .map((product) => ({ ...CommerceModel.liftLegacyProduct(product as any), $id: product.$id }))\n },\n [catalog],\n )\n\n const findByCode = useCallback(\n (code: string) => {\n const needle = code.trim().toLowerCase()\n if (!needle) return null\n for (const raw of catalog) {\n const product = { ...CommerceModel.liftLegacyProduct(raw as any), $id: raw.$id } as PosOfflineProduct\n const variant = (product['variants'] ?? []).find(\n (item: any) =>\n String(item?.barcode ?? '').trim().toLowerCase() === needle ||\n String(item?.sku ?? '').trim().toLowerCase() === needle,\n )\n if (variant) return { product, variant }\n }\n return null\n },\n [catalog],\n )\n\n const totalsFor = useCallback(\n (lines: RegisterLine[], discountPct: number) => {\n if (!kit?.available || !lines.length) return null\n return posOfflineSaleTotals({ lines, discountPct, tax: kit.tax })\n },\n [kit],\n )\n\n const ringInFlight = useRef(false)\n const ringCashSale = useCallback<PosOfflineState['ringCashSale']>(\n async (sale) => {\n const context = latest.current\n if (!store || !kit?.available || !uid || ringInFlight.current) return null\n if (sale.discountPct > kit.maxDiscountPct) return null\n const totals = posOfflineSaleTotals({ lines: sale.lines, discountPct: sale.discountPct, tax: kit.tax })\n if (sale.cashTenderedCents < totals.totalCents) return null\n ringInFlight.current = true\n try {\n const saleKey = newPosOfflineSaleKey()\n const soldAtMs = now()\n const byId = new Map(catalog.map((product) => [product.$id, product]))\n const lines: PosOfflineSaleLine[] = sale.lines.map((line) => {\n const product = byId.get(line.productId)\n const variant = (product?.['variants'] ?? []).find((item: any) => item?.id === line.variantId)\n return {\n productId: line.productId,\n ...(line.variantId ? { variantId: line.variantId } : {}),\n name: line.name,\n ...(line.variantLabel ? { variantLabel: line.variantLabel } : {}),\n ...(variant?.sku ? { sku: String(variant.sku) } : {}),\n ...(product?.['type'] ? { productType: product['type'] } : {}),\n quantity: line.quantity,\n unitAmountCents: line.unitAmountCents,\n ...(line.modifiers?.length ? { modifiers: line.modifiers } : {}),\n }\n })\n const shiftId = lastKnownShift(context.openShiftId, kit, registerId)\n const customer = sale.customer\n ? {\n ...(sale.customer.email ? { email: sale.customer.email } : {}),\n ...(sale.customer.name ? { name: sale.customer.name } : {}),\n ...(sale.customer.kind && sale.customer.kind !== 'none' && sale.customer.id\n ? { kind: sale.customer.kind, id: sale.customer.id }\n : {}),\n }\n : undefined\n const queued: PosOfflineSale = {\n v: 1,\n saleKey,\n hostId,\n orgId: kit.orgId,\n signedInUid: uid,\n registerId,\n shiftId,\n ...(context.locationId ? { locationId: context.locationId } : {}),\n ...(context.cashierAssertion ? { cashierAssertion: context.cashierAssertion } : {}),\n soldAtMs,\n lines,\n discountPct: sale.discountPct,\n totals,\n cashTenderedCents: sale.cashTenderedCents,\n changeCents: posOfflineChangeCents(totals.totalCents, sale.cashTenderedCents),\n ...(customer && Object.keys(customer).length ? { customer } : {}),\n }\n // The sale is real only once the device holds it: a write that fails\n // answers null, and the cashier is told before taking any cash.\n await store.putSale({ saleKey, sale: queued, state: 'queued', queuedAtMs: soldAtMs, attempts: 0 })\n setQueue(await store.listSales())\n return {\n saleKey,\n sale: queued,\n order: {\n status: 'paid',\n channel: 'pos',\n registerId,\n lineItems: lines.map((line) => ({ ...line, modifiers: undefined })) as CommerceModel.OrderLineItem[],\n totals,\n payments: [\n {\n id: 'pay_cash',\n method: 'cash',\n amountCents: totals.totalCents,\n status: 'succeeded',\n atMs: soldAtMs,\n cashTenderedCents: sale.cashTenderedCents,\n changeCents: queued.changeCents,\n },\n ],\n createdAtMs: soldAtMs,\n customerName: customer?.name ?? null,\n } as PosOfflineRungSale['order'],\n }\n } catch {\n return null\n } finally {\n ringInFlight.current = false\n }\n },\n [store, kit, uid, catalog, hostId, registerId, now],\n )\n\n const reportNetworkFailure = useCallback(() => setNetworkFailed(true), [])\n const dismissNotices = useCallback(() => setNotices([]), [])\n const discard = useCallback(\n async (saleKey: string) => {\n if (!store) return\n await store.removeSale(saleKey).catch(() => undefined)\n setQueue(await store.listSales().catch(() => []))\n },\n [store],\n )\n\n return useMemo(\n () => ({\n offline,\n ready,\n unavailableReason,\n kit,\n queue,\n syncing,\n syncError,\n notices,\n gridProducts,\n findByCode,\n totalsFor,\n ringCashSale,\n reportNetworkFailure,\n syncNow,\n dismissNotices,\n discard,\n }),\n [\n offline,\n ready,\n unavailableReason,\n kit,\n queue,\n syncing,\n syncError,\n notices,\n gridProducts,\n findByCode,\n totalsFor,\n ringCashSale,\n reportNetworkFailure,\n syncNow,\n dismissNotices,\n discard,\n ],\n )\n}\n"],"names":["useCallback","useEffect","useMemo","useRef","useState","CommerceModel","posOfflineChangeCents","posOfflineSaleTotals","POS_OFFLINE_SYNC_BATCH_MAX","fetchPosOfflineKit","newPosOfflineSaleKey","syncPosOfflineSales","openPosOfflineStore","posOfflineProduct","KIT_REFRESH_MS","CATALOG_REFRESH_MS","PROBE_MS","lastKnownShift","live","kit","registerId","undefined","registers","find","register","id","openShiftId","browserOnline","navigator","onLine","usePosOffline","input","hostId","user","uid","now","Date","openStore","store","setStore","storeFailed","setStoreFailed","setKit","catalog","setCatalog","queue","setQueue","navigatorOnline","setNavigatorOnline","networkFailed","setNetworkFailed","syncing","setSyncing","syncError","setSyncError","notices","setNotices","offline","latest","current","userRef","active","then","opened","saved","sales","Promise","all","loadKit","listSales","products","length","goOnline","goOffline","window","addEventListener","removeEventListener","kitSavedAt","catalogSavedAt","refreshKit","signedIn","answer","status","fresh","body","load","loadCatalog","map","filter","Boolean","catch","saveKit","kitSavedAtMs","catalogSavedAtMs","timer","setInterval","clearInterval","liveKey","liveProducts","product","String","join","byId","Map","$id","changed","trimmed","JSON","stringify","get","set","next","values","syncInFlight","syncNow","waiting","entry","state","sale","signedInUid","probe","ok","flagged","at","batch","slice","error","byKey","saleKey","result","results","removeSale","flags","push","orderId","number","stockConflicts","putSale","attempts","reason","unavailableReason","available","requireOpenShift","ready","gridProducts","search","categoryId","quickKeysId","typed","trim","toLowerCase","name","includes","Array","isArray","sort","a","b","localeCompare","liftLegacyProduct","findByCode","code","needle","raw","variant","item","barcode","sku","totalsFor","lines","discountPct","tax","ringInFlight","ringCashSale","context","maxDiscountPct","totals","cashTenderedCents","totalCents","soldAtMs","line","productId","variantId","variantLabel","productType","quantity","unitAmountCents","modifiers","shiftId","customer","email","kind","queued","v","orgId","locationId","cashierAssertion","changeCents","Object","keys","queuedAtMs","order","channel","lineItems","payments","method","amountCents","atMs","createdAtMs","customerName","reportNetworkFailure","dismissNotices","discard"],"mappings":"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"}
|
|
@@ -15,17 +15,33 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/ 'use client';
|
|
17
17
|
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
18
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
18
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
19
19
|
import { pluginDocsHelp } from "@aglyn/aglyn";
|
|
20
20
|
import { CardDisplay } from "@aglyn/shared-ui-jsx";
|
|
21
21
|
import { ListPagination } from "@aglyn/shared-ui-jsx/components/list-pagination.component";
|
|
22
|
+
import { ListQueryNotices } from "@aglyn/shared-ui-jsx/components/list-query-notices.component";
|
|
22
23
|
import { ListTable } from "@aglyn/shared-ui-jsx/components/list-table.component";
|
|
24
|
+
import { useListColumnSort } from "@aglyn/shared-ui-jsx/hooks/use-list-column-sort";
|
|
23
25
|
import { Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, MenuItem, Stack, TextField, Typography } from "@mui/material";
|
|
24
|
-
import { collection, limit,
|
|
25
|
-
import { useFirestore, useFirestoreCollection
|
|
26
|
+
import { collection, limit, query } from "firebase/firestore";
|
|
27
|
+
import { useFirestore, useFirestoreCollection } from "@aglyn/tenant-feature-instance";
|
|
28
|
+
import { useListQuery } from "@aglyn/tenant-feature-instance/hooks/use-list-query";
|
|
26
29
|
import { useEffect, useMemo, useState } from "react";
|
|
30
|
+
import { POS_SHIFT_COLUMN_SORTS, POS_SHIFT_LIST_QUERY } from "../../../constants/pos-shift-list-query.js";
|
|
27
31
|
import { posMoney, posShiftsCsv } from "../../../model/commerce-pos-ops.js";
|
|
28
32
|
import { PosShiftReportView } from "./pos-shift-report.component.js";
|
|
33
|
+
/**
|
|
34
|
+
* "By" is whichever name the shift carries, so it sorts the page on screen
|
|
35
|
+
* (AGL-3680); every other column is the query's order (`POS_SHIFT_COLUMN_SORTS`).
|
|
36
|
+
*/ const SHIFT_PAGE_SORTS = {
|
|
37
|
+
by: (row)=>{
|
|
38
|
+
var _ref, _row_closedByName;
|
|
39
|
+
return (_ref = (_row_closedByName = row.closedByName) != null ? _row_closedByName : row.openedByName) != null ? _ref : '';
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const SHIFT_PAGE_SORT_HEADERS = {
|
|
43
|
+
by: 'By'
|
|
44
|
+
};
|
|
29
45
|
/** A shift's time as the history lists it, in the viewer's locale. */ const when = (ms)=>ms ? new Date(ms).toLocaleString([], {
|
|
30
46
|
month: 'short',
|
|
31
47
|
day: 'numeric',
|
|
@@ -62,13 +78,39 @@ import { PosShiftReportView } from "./pos-shift-report.component.js";
|
|
|
62
78
|
registerId,
|
|
63
79
|
registers
|
|
64
80
|
]);
|
|
65
|
-
|
|
81
|
+
/*
|
|
82
|
+
* Every header sorts (AGL-3680): the asked order is the query's, over every
|
|
83
|
+
* shift the register has, newest first until a header is clicked.
|
|
84
|
+
*/ const [askedSort, setAskedSort] = useState(POS_SHIFT_COLUMN_SORTS[0]);
|
|
85
|
+
const shiftsRef = useMemo(()=>registerId ? collection(firestore, 'hosts', hostId, 'registers', registerId, 'shifts') : null, [
|
|
66
86
|
firestore,
|
|
67
87
|
hostId,
|
|
68
88
|
registerId
|
|
69
|
-
]
|
|
89
|
+
]);
|
|
90
|
+
const { rows: shifts, hasMore, page, setPage, pageSize, setPageSize, status, plan } = useListQuery({
|
|
91
|
+
collection: shiftsRef,
|
|
92
|
+
declaration: POS_SHIFT_LIST_QUERY,
|
|
93
|
+
request: {
|
|
94
|
+
clauses: [],
|
|
95
|
+
sort: askedSort
|
|
96
|
+
},
|
|
97
|
+
deps: [
|
|
98
|
+
firestore,
|
|
99
|
+
hostId,
|
|
100
|
+
registerId
|
|
101
|
+
],
|
|
70
102
|
idField: '$id'
|
|
71
103
|
});
|
|
104
|
+
const columnSort = useListColumnSort({
|
|
105
|
+
sorts: POS_SHIFT_COLUMN_SORTS,
|
|
106
|
+
defaultSort: POS_SHIFT_COLUMN_SORTS[0],
|
|
107
|
+
sort: askedSort,
|
|
108
|
+
onSortChange: setAskedSort,
|
|
109
|
+
orderBy: plan.orderBy,
|
|
110
|
+
rows: shifts,
|
|
111
|
+
pageSorts: SHIFT_PAGE_SORTS,
|
|
112
|
+
headers: SHIFT_PAGE_SORT_HEADERS
|
|
113
|
+
});
|
|
72
114
|
const [selected, setSelected] = useState(null);
|
|
73
115
|
const registerName = (_ref = (_registers_find = registers.find((register)=>register.$id === registerId)) == null ? void 0 : _registers_find.name) != null ? _ref : '';
|
|
74
116
|
const columns = useMemo(()=>[
|
|
@@ -104,7 +146,10 @@ import { PosShiftReportView } from "./pos-shift-report.component.js";
|
|
|
104
146
|
headerName: 'Net sales',
|
|
105
147
|
type: 'number',
|
|
106
148
|
minWidth: 110,
|
|
107
|
-
valueGetter: (_value, row)=>
|
|
149
|
+
valueGetter: (_value, row)=>{
|
|
150
|
+
var _row_netSalesCents;
|
|
151
|
+
return row.report ? posMoney((_row_netSalesCents = row.netSalesCents) != null ? _row_netSalesCents : row.report.netSalesCents) : '—';
|
|
152
|
+
}
|
|
108
153
|
},
|
|
109
154
|
{
|
|
110
155
|
field: 'expectedCashCents',
|
|
@@ -186,20 +231,30 @@ import { PosShiftReportView } from "./pos-shift-report.component.js";
|
|
|
186
231
|
children: register.name
|
|
187
232
|
}, register.$id))
|
|
188
233
|
}) : null,
|
|
189
|
-
status === 'success' && !shifts.length ? /*#__PURE__*/ _jsx(Typography, {
|
|
234
|
+
status === 'success' && !shifts.length && askedSort === POS_SHIFT_COLUMN_SORTS[0] ? /*#__PURE__*/ _jsx(Typography, {
|
|
190
235
|
variant: "body2",
|
|
191
236
|
color: "text.secondary",
|
|
192
237
|
children: 'No shifts yet. Open one from the register before the first sale of the day.'
|
|
193
|
-
}) : /*#__PURE__*/
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
238
|
+
}) : /*#__PURE__*/ _jsxs(_Fragment, {
|
|
239
|
+
children: [
|
|
240
|
+
/*#__PURE__*/ _jsx(ListQueryNotices, {
|
|
241
|
+
refused: [],
|
|
242
|
+
notices: [
|
|
243
|
+
...plan.notices,
|
|
244
|
+
...columnSort.notices
|
|
245
|
+
]
|
|
246
|
+
}),
|
|
247
|
+
/*#__PURE__*/ _jsx(ListTable, {
|
|
248
|
+
"aria-label": "Shift history",
|
|
249
|
+
rows: columnSort.rows,
|
|
250
|
+
columns: columns,
|
|
251
|
+
// The query orders and pages the shifts, or the page does for "By".
|
|
252
|
+
columnSort: columnSort,
|
|
253
|
+
hideFooter: true,
|
|
254
|
+
noRowsLabel: "No shifts yet",
|
|
255
|
+
onOpen: (_id, row)=>row.report && setSelected(row)
|
|
256
|
+
})
|
|
257
|
+
]
|
|
203
258
|
}),
|
|
204
259
|
/*#__PURE__*/ _jsx(ListPagination, {
|
|
205
260
|
page: page,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../libs/plugins/commerce/src/lib/components/console/pos-ops/pos-shift-history-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { ListTable } from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport {\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { collection, limit, orderBy, query } from 'firebase/firestore'\nimport {\n useFirestore,\n useFirestoreCollection,\n usePagedCollection,\n} from '@aglyn/tenant-feature-instance'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { useEffect, useMemo, useState } from 'react'\nimport { posMoney, posShiftsCsv, type PosShift } from '../../../model/commerce-pos-ops'\nimport { PosShiftReportView } from './pos-shift-report.component'\n\nexport interface PosShiftHistoryCardProps {\n hostId: string\n}\n\ntype ShiftRow = PosShift & { $id: string }\n\n/** A shift's time as the history lists it, in the viewer's locale. */\nconst when = (ms?: number) =>\n ms\n ? new Date(ms).toLocaleString([], {\n month: 'short',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n })\n : '—'\n\n/**\n * Shift history (AGL-3609): one register's shifts, newest first, from an\n * ordered Firestore query — open ones included — with each closed shift's Z\n * report a click away and the shifts on the page exportable as a spreadsheet.\n */\nexport function PosShiftHistoryCard(props: PosShiftHistoryCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: registerDocs, status: registersStatus } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'registers'), limit(25)),\n [firestore, hostId],\n { idField: '$id' },\n )\n const registers = useMemo(\n () =>\n [...(registerDocs ?? [])].sort((a: any, b: any) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n ),\n [registerDocs],\n )\n const [registerId, setRegisterId] = useState('')\n useEffect(() => {\n if (!registerId && registers.length) setRegisterId(registers[0].$id)\n }, [registerId, registers])\n const {\n rows: shifts,\n hasMore,\n page,\n setPage,\n pageSize,\n setPageSize,\n status,\n } = usePagedCollection<ShiftRow>(\n (pageLimit) =>\n registerId\n ? query(\n collection(firestore, 'hosts', hostId, 'registers', registerId, 'shifts'),\n orderBy('openedAtMs', 'desc'),\n limit(pageLimit),\n )\n : null,\n [firestore, hostId, registerId],\n { idField: '$id' },\n )\n const [selected, setSelected] = useState<ShiftRow | null>(null)\n const registerName = registers.find((register: any) => register.$id === registerId)?.name ?? ''\n\n const columns = useMemo(\n () =>\n [\n {\n field: 'openedAtMs',\n headerName: 'Opened',\n flex: 1,\n minWidth: 150,\n valueFormatter: (value: number) => when(value),\n },\n {\n field: 'closedAtMs',\n headerName: 'Closed',\n flex: 1,\n minWidth: 150,\n renderCell: ({ row }: { row: ShiftRow }) =>\n row.status === 'open' ? <Chip size=\"small\" color=\"success\" label=\"Open\" /> : when(row.closedAtMs),\n },\n {\n field: 'by',\n headerName: 'By',\n minWidth: 140,\n valueGetter: (_value: unknown, row: ShiftRow) => row.closedByName ?? row.openedByName ?? '',\n },\n {\n field: 'netSales',\n headerName: 'Net sales',\n type: 'number',\n minWidth: 110,\n valueGetter: (_value: unknown, row: ShiftRow) => (row.report ? posMoney(row.report.netSalesCents) : '—'),\n },\n {\n field: 'expectedCashCents',\n headerName: 'Expected',\n type: 'number',\n minWidth: 110,\n valueFormatter: (value: number | undefined) => (value != null ? posMoney(value) : '—'),\n },\n {\n field: 'countedCashCents',\n headerName: 'Counted',\n type: 'number',\n minWidth: 110,\n valueFormatter: (value: number | undefined) => (value != null ? posMoney(value) : '—'),\n },\n {\n field: 'varianceCents',\n headerName: 'Variance',\n minWidth: 110,\n align: 'right',\n headerAlign: 'right',\n renderCell: ({ row }: { row: ShiftRow }) =>\n row.varianceCents != null ? (\n <Chip\n size=\"small\"\n variant=\"outlined\"\n color={row.varianceCents === 0 ? 'success' : 'warning'}\n label={posMoney(row.varianceCents)}\n />\n ) : (\n '—'\n ),\n },\n ] as GridColDef[],\n [],\n )\n\n const exportCsv = () => {\n const csv = posShiftsCsv(\n shifts.map((shift) => ({ ...shift, id: shift.$id, registerName })),\n )\n const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' })\n const url = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.href = url\n link.download = `shifts-${registerName.replace(/[^A-Za-z0-9_-]+/g, '-') || registerId}.csv`\n link.click()\n window.setTimeout(() => URL.revokeObjectURL(url), 1000)\n }\n\n // A store without a register has no shifts to show.\n if (registersStatus === 'success' && !registers.length) return null\n\n return (\n <CardDisplay\n header={'Shift history'}\n help={pluginDocsHelp('posOperations', { anchor: '#shifts-and-the-cash-drawer' })}\n HeaderProps={{\n action: (\n <Button size=\"small\" onClick={exportCsv} disabled={!shifts.length}>\n {'Export CSV'}\n </Button>\n ),\n }}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1.5}>\n {registers.length > 1 ? (\n <TextField\n select\n size=\"small\"\n label=\"Register\"\n value={registerId}\n onChange={(event) => setRegisterId(event.target.value)}\n sx={{ maxWidth: 280 }}\n >\n {registers.map((register: any) => (\n <MenuItem key={register.$id} value={register.$id}>\n {register.name}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n {status === 'success' && !shifts.length ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No shifts yet. Open one from the register before the first sale of the day.'}\n </Typography>\n ) : (\n <ListTable\n aria-label=\"Shift history\"\n rows={shifts}\n columns={columns}\n // The query orders and pages the shifts; the grid only draws them.\n sortingMode=\"server\"\n disableColumnSorting\n hideFooter\n noRowsLabel=\"No shifts yet\"\n onOpen={(_id, row) => (row as ShiftRow).report && setSelected(row as ShiftRow)}\n />\n )}\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={shifts.length}\n hasMore={hasMore}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n </Stack>\n <Dialog open={Boolean(selected)} onClose={() => setSelected(null)} maxWidth=\"xs\" fullWidth>\n <DialogTitle>{`Z report · ${when(selected?.closedAtMs)}`}</DialogTitle>\n <DialogContent>\n {selected?.report ? <PosShiftReportView report={selected.report} shift={selected} /> : null}\n {selected?.closingNote ? (\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ mt: 2 }}>\n {selected.closingNote}\n </Typography>\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setSelected(null)}>{'Done'}</Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\n\nPosShiftHistoryCard.displayName = 'PosShiftHistoryCard'\n\nexport default PosShiftHistoryCard\n"],"names":["pluginDocsHelp","CardDisplay","ListPagination","ListTable","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","Typography","collection","limit","orderBy","query","useFirestore","useFirestoreCollection","usePagedCollection","useEffect","useMemo","useState","posMoney","posShiftsCsv","PosShiftReportView","when","ms","Date","toLocaleString","month","day","hour","minute","PosShiftHistoryCard","props","registers","hostId","firestore","data","registerDocs","status","registersStatus","idField","sort","a","b","String","name","localeCompare","registerId","setRegisterId","length","$id","rows","shifts","hasMore","page","setPage","pageSize","setPageSize","pageLimit","selected","setSelected","registerName","find","register","columns","field","headerName","flex","minWidth","valueFormatter","value","renderCell","row","size","color","label","closedAtMs","valueGetter","_value","closedByName","openedByName","type","report","netSalesCents","align","headerAlign","varianceCents","variant","exportCsv","csv","map","shift","id","blob","Blob","url","URL","createObjectURL","link","document","createElement","href","download","replace","click","window","setTimeout","revokeObjectURL","header","help","anchor","HeaderProps","action","onClick","disabled","contentGutterX","contentGutterY","spacing","select","onChange","event","target","sx","maxWidth","aria-label","sortingMode","disableColumnSorting","hideFooter","noRowsLabel","onOpen","_id","rowCount","onPageChange","onPageSizeChange","open","Boolean","onClose","fullWidth","closingNote","mt","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../libs/plugins/commerce/src/lib/components/console/pos-ops/pos-shift-history-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { ListQueryNotices } from '@aglyn/shared-ui-jsx/components/list-query-notices.component'\nimport { ListTable } from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport type { ListQuerySort } from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport { useListColumnSort } from '@aglyn/shared-ui-jsx/hooks/use-list-column-sort'\nimport {\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { collection, limit, query } from 'firebase/firestore'\nimport { useFirestore, useFirestoreCollection } from '@aglyn/tenant-feature-instance'\nimport { useListQuery } from '@aglyn/tenant-feature-instance/hooks/use-list-query'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { useEffect, useMemo, useState } from 'react'\nimport {\n POS_SHIFT_COLUMN_SORTS,\n POS_SHIFT_LIST_QUERY,\n} from '../../../constants/pos-shift-list-query'\nimport { posMoney, posShiftsCsv, type PosShift } from '../../../model/commerce-pos-ops'\nimport { PosShiftReportView } from './pos-shift-report.component'\n\nexport interface PosShiftHistoryCardProps {\n hostId: string\n}\n\ntype ShiftRow = PosShift & { $id: string }\n\n/**\n * \"By\" is whichever name the shift carries, so it sorts the page on screen\n * (AGL-3680); every other column is the query's order (`POS_SHIFT_COLUMN_SORTS`).\n */\nconst SHIFT_PAGE_SORTS = {\n by: (row: ShiftRow) => row.closedByName ?? row.openedByName ?? '',\n}\nconst SHIFT_PAGE_SORT_HEADERS = { by: 'By' }\n\n/** A shift's time as the history lists it, in the viewer's locale. */\nconst when = (ms?: number | null) =>\n ms\n ? new Date(ms).toLocaleString([], {\n month: 'short',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n })\n : '—'\n\n/**\n * Shift history (AGL-3609): one register's shifts, newest first, from an\n * ordered Firestore query — open ones included — with each closed shift's Z\n * report a click away and the shifts on the page exportable as a spreadsheet.\n */\nexport function PosShiftHistoryCard(props: PosShiftHistoryCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: registerDocs, status: registersStatus } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'registers'), limit(25)),\n [firestore, hostId],\n { idField: '$id' },\n )\n const registers = useMemo(\n () =>\n [...(registerDocs ?? [])].sort((a: any, b: any) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n ),\n [registerDocs],\n )\n const [registerId, setRegisterId] = useState('')\n useEffect(() => {\n if (!registerId && registers.length) setRegisterId(registers[0].$id)\n }, [registerId, registers])\n /*\n * Every header sorts (AGL-3680): the asked order is the query's, over every\n * shift the register has, newest first until a header is clicked.\n */\n const [askedSort, setAskedSort] = useState<ListQuerySort | null>(POS_SHIFT_COLUMN_SORTS[0])\n const shiftsRef = useMemo(\n () =>\n registerId\n ? collection(firestore, 'hosts', hostId, 'registers', registerId, 'shifts')\n : null,\n [firestore, hostId, registerId],\n )\n const {\n rows: shifts,\n hasMore,\n page,\n setPage,\n pageSize,\n setPageSize,\n status,\n plan,\n } = useListQuery<ShiftRow>({\n collection: shiftsRef,\n declaration: POS_SHIFT_LIST_QUERY,\n request: { clauses: [], sort: askedSort },\n deps: [firestore, hostId, registerId],\n idField: '$id',\n })\n const columnSort = useListColumnSort<ShiftRow>({\n sorts: POS_SHIFT_COLUMN_SORTS,\n defaultSort: POS_SHIFT_COLUMN_SORTS[0],\n sort: askedSort,\n onSortChange: setAskedSort,\n orderBy: plan.orderBy,\n rows: shifts,\n pageSorts: SHIFT_PAGE_SORTS,\n headers: SHIFT_PAGE_SORT_HEADERS,\n })\n const [selected, setSelected] = useState<ShiftRow | null>(null)\n const registerName = registers.find((register: any) => register.$id === registerId)?.name ?? ''\n\n const columns = useMemo(\n () =>\n [\n {\n field: 'openedAtMs',\n headerName: 'Opened',\n flex: 1,\n minWidth: 150,\n valueFormatter: (value: number) => when(value),\n },\n {\n field: 'closedAtMs',\n headerName: 'Closed',\n flex: 1,\n minWidth: 150,\n renderCell: ({ row }: { row: ShiftRow }) =>\n row.status === 'open' ? <Chip size=\"small\" color=\"success\" label=\"Open\" /> : when(row.closedAtMs),\n },\n {\n field: 'by',\n headerName: 'By',\n minWidth: 140,\n valueGetter: (_value: unknown, row: ShiftRow) => row.closedByName ?? row.openedByName ?? '',\n },\n {\n field: 'netSales',\n headerName: 'Net sales',\n type: 'number',\n minWidth: 110,\n valueGetter: (_value: unknown, row: ShiftRow) =>\n row.report ? posMoney(row.netSalesCents ?? row.report.netSalesCents) : '—',\n },\n {\n field: 'expectedCashCents',\n headerName: 'Expected',\n type: 'number',\n minWidth: 110,\n valueFormatter: (value: number | null | undefined) => (value != null ? posMoney(value) : '—'),\n },\n {\n field: 'countedCashCents',\n headerName: 'Counted',\n type: 'number',\n minWidth: 110,\n valueFormatter: (value: number | null | undefined) => (value != null ? posMoney(value) : '—'),\n },\n {\n field: 'varianceCents',\n headerName: 'Variance',\n minWidth: 110,\n align: 'right',\n headerAlign: 'right',\n renderCell: ({ row }: { row: ShiftRow }) =>\n row.varianceCents != null ? (\n <Chip\n size=\"small\"\n variant=\"outlined\"\n color={row.varianceCents === 0 ? 'success' : 'warning'}\n label={posMoney(row.varianceCents)}\n />\n ) : (\n '—'\n ),\n },\n ] as GridColDef[],\n [],\n )\n\n const exportCsv = () => {\n const csv = posShiftsCsv(\n shifts.map((shift) => ({ ...shift, id: shift.$id, registerName })),\n )\n const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' })\n const url = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.href = url\n link.download = `shifts-${registerName.replace(/[^A-Za-z0-9_-]+/g, '-') || registerId}.csv`\n link.click()\n window.setTimeout(() => URL.revokeObjectURL(url), 1000)\n }\n\n // A store without a register has no shifts to show.\n if (registersStatus === 'success' && !registers.length) return null\n\n return (\n <CardDisplay\n header={'Shift history'}\n help={pluginDocsHelp('posOperations', { anchor: '#shifts-and-the-cash-drawer' })}\n HeaderProps={{\n action: (\n <Button size=\"small\" onClick={exportCsv} disabled={!shifts.length}>\n {'Export CSV'}\n </Button>\n ),\n }}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1.5}>\n {registers.length > 1 ? (\n <TextField\n select\n size=\"small\"\n label=\"Register\"\n value={registerId}\n onChange={(event) => setRegisterId(event.target.value)}\n sx={{ maxWidth: 280 }}\n >\n {registers.map((register: any) => (\n <MenuItem key={register.$id} value={register.$id}>\n {register.name}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n {/* Empty only in the default order: a header that came back empty keeps the grid to click another. */}\n {status === 'success' && !shifts.length && askedSort === POS_SHIFT_COLUMN_SORTS[0] ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No shifts yet. Open one from the register before the first sale of the day.'}\n </Typography>\n ) : (\n <>\n <ListQueryNotices refused={[]} notices={[...plan.notices, ...columnSort.notices]} />\n <ListTable\n aria-label=\"Shift history\"\n rows={columnSort.rows}\n columns={columns}\n // The query orders and pages the shifts, or the page does for \"By\".\n columnSort={columnSort}\n hideFooter\n noRowsLabel=\"No shifts yet\"\n onOpen={(_id, row) => (row as ShiftRow).report && setSelected(row as ShiftRow)}\n />\n </>\n )}\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={shifts.length}\n hasMore={hasMore}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n </Stack>\n <Dialog open={Boolean(selected)} onClose={() => setSelected(null)} maxWidth=\"xs\" fullWidth>\n <DialogTitle>{`Z report · ${when(selected?.closedAtMs)}`}</DialogTitle>\n <DialogContent>\n {selected?.report ? <PosShiftReportView report={selected.report} shift={selected} /> : null}\n {selected?.closingNote ? (\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ mt: 2 }}>\n {selected.closingNote}\n </Typography>\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setSelected(null)}>{'Done'}</Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\n\nPosShiftHistoryCard.displayName = 'PosShiftHistoryCard'\n\nexport default PosShiftHistoryCard\n"],"names":["pluginDocsHelp","CardDisplay","ListPagination","ListQueryNotices","ListTable","useListColumnSort","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","Typography","collection","limit","query","useFirestore","useFirestoreCollection","useListQuery","useEffect","useMemo","useState","POS_SHIFT_COLUMN_SORTS","POS_SHIFT_LIST_QUERY","posMoney","posShiftsCsv","PosShiftReportView","SHIFT_PAGE_SORTS","by","row","closedByName","openedByName","SHIFT_PAGE_SORT_HEADERS","when","ms","Date","toLocaleString","month","day","hour","minute","PosShiftHistoryCard","props","registers","hostId","firestore","data","registerDocs","status","registersStatus","idField","sort","a","b","String","name","localeCompare","registerId","setRegisterId","length","$id","askedSort","setAskedSort","shiftsRef","rows","shifts","hasMore","page","setPage","pageSize","setPageSize","plan","declaration","request","clauses","deps","columnSort","sorts","defaultSort","onSortChange","orderBy","pageSorts","headers","selected","setSelected","registerName","find","register","columns","field","headerName","flex","minWidth","valueFormatter","value","renderCell","size","color","label","closedAtMs","valueGetter","_value","type","report","netSalesCents","align","headerAlign","varianceCents","variant","exportCsv","csv","map","shift","id","blob","Blob","url","URL","createObjectURL","link","document","createElement","href","download","replace","click","window","setTimeout","revokeObjectURL","header","help","anchor","HeaderProps","action","onClick","disabled","contentGutterX","contentGutterY","spacing","select","onChange","event","target","sx","maxWidth","refused","notices","aria-label","hideFooter","noRowsLabel","onOpen","_id","rowCount","onPageChange","onPageSizeChange","open","Boolean","onClose","fullWidth","closingNote","mt","displayName"],"mappings":"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"}
|