@aglyn/plugins-commerce 1.0.0-beta.199 → 1.0.0-beta.201
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/console/catalog-organization-card.component.js +48 -3
- package/src/lib/components/console/catalog-organization-card.component.js.map +1 -1
- package/src/lib/components/console/gift-cards-card.component.d.ts +9 -0
- package/src/lib/components/console/gift-cards-card.component.js +53 -28
- package/src/lib/components/console/gift-cards-card.component.js.map +1 -1
- package/src/lib/components/console/host-orders-card.component.d.ts +26 -0
- package/src/lib/components/console/host-orders-card.component.js +264 -194
- package/src/lib/components/console/host-orders-card.component.js.map +1 -1
- package/src/lib/components/console/order-detail-dialog.component.js +12 -0
- package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
- package/src/lib/components/console/pos-page.component.d.ts +14 -0
- package/src/lib/components/console/pos-page.component.js +86 -45
- package/src/lib/components/console/pos-page.component.js.map +1 -1
- package/src/lib/components/console/product-editor-dialog.component.js +30 -6
- package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
- package/src/lib/components/console/product-slugs.d.ts +43 -0
- package/src/lib/components/console/product-slugs.js +60 -0
- package/src/lib/components/console/product-slugs.js.map +1 -0
- package/src/lib/components/console/products-hub-card.component.d.ts +2 -2
- package/src/lib/components/console/products-hub-card.component.js +229 -282
- package/src/lib/components/console/products-hub-card.component.js.map +1 -1
- package/src/lib/components/console/products-hub-zone.component.d.ts +10 -5
- package/src/lib/components/console/products-hub-zone.component.js +32 -9
- package/src/lib/components/console/products-hub-zone.component.js.map +1 -1
- package/src/lib/components/console/recovery-queue-card.component.d.ts +37 -5
- package/src/lib/components/console/recovery-queue-card.component.js +107 -86
- package/src/lib/components/console/recovery-queue-card.component.js.map +1 -1
- package/src/lib/components/console/reservations-card.component.js +15 -3
- package/src/lib/components/console/reservations-card.component.js.map +1 -1
- package/src/lib/components/console/smart-collections.d.ts +32 -0
- package/src/lib/components/console/smart-collections.js +57 -0
- package/src/lib/components/console/smart-collections.js.map +1 -0
- package/src/lib/components/console/stock-movements-card.component.d.ts +13 -4
- package/src/lib/components/console/stock-movements-card.component.js +142 -180
- package/src/lib/components/console/stock-movements-card.component.js.map +1 -1
- package/src/lib/components/product-grid.d.ts +5 -0
- package/src/lib/components/product-grid.js +95 -34
- package/src/lib/components/product-grid.js.map +1 -1
- package/src/lib/constants/orders-list-query.d.ts +40 -0
- package/src/lib/constants/orders-list-query.js +205 -0
- package/src/lib/constants/orders-list-query.js.map +1 -0
- package/src/lib/constants/product-list-query.d.ts +42 -0
- package/src/lib/constants/product-list-query.js +217 -0
- package/src/lib/constants/product-list-query.js.map +1 -0
- package/src/lib/constants/stock-movement-query.d.ts +27 -0
- package/src/lib/constants/stock-movement-query.js +110 -0
- package/src/lib/constants/stock-movement-query.js.map +1 -0
- package/src/lib/constants/storefront-catalog-query.d.ts +103 -0
- package/src/lib/constants/storefront-catalog-query.js +312 -0
- package/src/lib/constants/storefront-catalog-query.js.map +1 -0
- package/src/lib/model/checkout-recovery.d.ts +37 -0
- package/src/lib/model/checkout-recovery.js +44 -0
- package/src/lib/model/checkout-recovery.js.map +1 -0
- package/src/lib/model/commerce-orders.d.ts +7 -0
- package/src/lib/model/commerce-orders.js.map +1 -1
- package/src/lib/model/commerce.d.ts +80 -0
- package/src/lib/model/commerce.js +67 -6
- package/src/lib/model/commerce.js.map +1 -1
- package/src/lib/model/gift-card-search.d.ts +24 -0
- package/src/lib/model/gift-card-search.js +48 -0
- package/src/lib/model/gift-card-search.js.map +1 -0
- package/src/lib/model/index.d.ts +1 -0
- package/src/lib/model/index.js +1 -0
- package/src/lib/model/index.js.map +1 -1
- package/src/lib/model/order-list-fields.d.ts +80 -0
- package/src/lib/model/order-list-fields.js +112 -0
- package/src/lib/model/order-list-fields.js.map +1 -0
- package/src/lib/server/billing-webhook.js +180 -99
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/cancel-order.js +6 -6
- package/src/lib/server/cancel-order.js.map +1 -1
- package/src/lib/server/cart-checkout.js +9 -2
- package/src/lib/server/cart-checkout.js.map +1 -1
- package/src/lib/server/catalog.d.ts +46 -30
- package/src/lib/server/catalog.js +507 -170
- package/src/lib/server/catalog.js.map +1 -1
- package/src/lib/server/checkout.js +3 -1
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/collection-membership.d.ts +35 -0
- package/src/lib/server/collection-membership.js +172 -0
- package/src/lib/server/collection-membership.js.map +1 -0
- package/src/lib/server/draft-order.js +5 -3
- package/src/lib/server/draft-order.js.map +1 -1
- package/src/lib/server/gift-cards.js +7 -1
- package/src/lib/server/gift-cards.js.map +1 -1
- package/src/lib/server/member-name-search.d.ts +17 -0
- package/src/lib/server/member-name-search.js +23 -1
- package/src/lib/server/member-name-search.js.map +1 -1
- package/src/lib/server/membership-account.js +7 -3
- package/src/lib/server/membership-account.js.map +1 -1
- package/src/lib/server/membership-admin-password.js +8 -2
- package/src/lib/server/membership-admin-password.js.map +1 -1
- package/src/lib/server/membership-recover.js +4 -1
- package/src/lib/server/membership-recover.js.map +1 -1
- package/src/lib/server/membership-register.js +3 -1
- package/src/lib/server/membership-register.js.map +1 -1
- package/src/lib/server/pos-order.js +9 -5
- package/src/lib/server/pos-order.js.map +1 -1
- package/src/lib/server/process-abandoned.js +20 -7
- package/src/lib/server/process-abandoned.js.map +1 -1
- package/src/lib/server/process-restock.js +6 -2
- package/src/lib/server/process-restock.js.map +1 -1
- package/src/lib/server/reserve-stock.js +3 -4
- package/src/lib/server/reserve-stock.js.map +1 -1
- package/src/lib/server/reserve.js +3 -1
- package/src/lib/server/reserve.js.map +1 -1
- package/src/lib/server/site-page-enricher.js +3 -3
- package/src/lib/server/site-page-enricher.js.map +1 -1
- package/src/lib/server.js +4 -0
- package/src/lib/server.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/reservations-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'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport * as CommerceModel from '../../model'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\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 {\n collection,\n deleteDoc,\n doc,\n limit,\n orderBy,\n query,\n setDoc,\n updateDoc,\n} from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport {\n useFirestoreCollection,\n usePagedCollection,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\n\nexport interface ReservationsCardProps {\n hostId: string\n}\n\nconst STATUS_COLOR: Record<\n string,\n 'default' | 'success' | 'warning' | 'error' | 'info'\n> = {\n pending: 'warning',\n confirmed: 'info',\n checked_in: 'success',\n checked_out: 'default',\n cancelled: 'default',\n no_show: 'error',\n}\n\nconst usd = (cents: number | undefined) =>\n `$${((cents ?? 0) / 100).toFixed(2)}`\nconst day = (dayMs: number | undefined) =>\n dayMs ? new Date(dayMs).toISOString().slice(0, 10) : '—'\n\n/**\n * Reservations console (AGL-311): resources CRUD (nightly pricing,\n * seasons via multipliers, deposits) plus the reservations list with\n * check-in/out, no-show, cancel, and walk-in creation. The storefront\n * widget books against the same docs.\n */\nexport function ReservationsCard(props: ReservationsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const {\n data: resourceDocs,\n status: resourcesStatus,\n /**\n * The rows this editor is seeded from are unconfirmed by the server\n * (AGL-1358). Editing spreads a whole stored resource into\n * `resourceDraft` and writes all of it back, so `merge: true` protects\n * nothing. `blocks` is the field that costs more than money: it is the\n * manually blocked date ranges `isRangeAvailable` reads, so a cached seed\n * re-opens dates that were closed and lets the next guest book a room\n * that is already spoken for.\n */\n fromCache: resourcesFromCache,\n } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'resources'), limit(50)),\n [firestore, hostId],\n { idField: '$id' },\n )\n /*\n * Ordered by the server, and a growing window rather than a fixed 300.\n *\n * `limit(300)` carried no `orderBy`, so Firestore returned DOCUMENT-ID\n * order and the client sort below arranged that pseudo-random sample by\n * check-in. The ordering is unchanged — earliest check-in first, exactly\n * what the sort did — it is now the QUERY's, so the window is the real\n * front of the list and the rest can be reached.\n */\n const {\n rows: reservations,\n hasMore,\n page,\n setPage,\n pageSize,\n setPageSize,\n } = usePagedCollection<any>(\n (pageLimit) =>\n query(\n collection(firestore, 'hosts', hostId, 'reservations'),\n orderBy('checkInDayMs', 'asc'),\n limit(pageLimit),\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const resourceNames = useMemo(() => {\n const map: Record<string, string> = {}\n for (const resource of resourceDocs ?? []) {\n map[resource.$id] = resource.name\n }\n return map\n }, [resourceDocs])\n\n const [resourceDraft, setResourceDraft] = useState<\n (Partial<CommerceModel.HostResource> & { id: string | null }) | null\n >(null)\n const [walkIn, setWalkIn] = useState<{\n resourceId: string\n checkIn: string\n checkOut: string\n guestName: string\n } | null>(null)\n\n const handleResourceSave = useCallback(async () => {\n if (!resourceDraft?.name?.trim() || !(resourceDraft.nightlyRateUsd! > 0)) {\n return\n }\n /**\n * `$id` comes off with `id` (AGL-1374).\n *\n * The editor is seeded `{ id: resource.$id, ...resource }`, and that\n * spread carries the listener's SYNTHETIC `$id` — `idField: '$id'`\n * stamps the document id onto the in-memory row, where nothing persists\n * it. Destructuring only `id` left `$id` in `data`, so every save wrote\n * it into the stored document as a real field. Nothing reads it; it is\n * the listener's bookkeeping leaking into storage, and once there no\n * reader can tell it from a field the card meant to write.\n */\n const seeded = resourceDraft as typeof resourceDraft & { $id?: string }\n const { id, $id: _syntheticId, ...data } = seeded\n /**\n * Refuse an EDIT whose seed the server never confirmed (AGL-1358).\n * `data` is the whole stored row spread out of the listener, so\n * `merge: true` has nothing left to protect — every field is present,\n * `blocks` and the seasonal multipliers included.\n *\n * Only the edit path. A NEW resource is built from defaults at a fresh\n * uid and can overwrite nothing, and the first snapshot of any listener\n * is `fromCache: true`, so guarding a create would refuse a save that was\n * never unsafe.\n *\n * The guard WRAPS the write — an early return is a shape you can keep\n * while losing the protection.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'resource',\n unreadable: Boolean(id) && resourcesStatus === 'error',\n fromCache: Boolean(id) && resourcesFromCache,\n },\n async () => {\n await setDoc(\n doc(\n firestore,\n 'hosts',\n hostId,\n 'resources',\n id ?? Aglyn.createResourceUid(),\n ),\n {\n ...data,\n name: resourceDraft.name!.trim().slice(0, 120),\n },\n { merge: true },\n )\n },\n )\n // Before `setResourceDraft(null)`, so a refusal keeps the dialog open\n // with what was typed.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n setResourceDraft(null)\n enqueueSnackbar('Resource saved', { variant: 'success', persist: false })\n }, [\n resourceDraft,\n firestore,\n hostId,\n enqueueSnackbar,\n resourcesStatus,\n resourcesFromCache,\n ])\n\n const handleStatus = useCallback(\n (reservation: any, status: CommerceModel.ReservationStatus) => async () => {\n await updateDoc(\n doc(firestore, 'hosts', hostId, 'reservations', reservation.$id),\n { status },\n )\n },\n [firestore, hostId],\n )\n\n const handleWalkIn = useCallback(async () => {\n if (!walkIn?.resourceId || !walkIn.checkIn || !walkIn.checkOut) return\n const resource = (resourceDocs ?? []).find(\n (item: any) => item.$id === walkIn.resourceId,\n ) as CommerceModel.HostResource | undefined\n if (!resource) return\n const checkInDayMs = Date.parse(`${walkIn.checkIn}T00:00:00Z`)\n const checkOutDayMs = Date.parse(`${walkIn.checkOut}T00:00:00Z`)\n // `createdAtMs` carried through because the pending hold lapses on it\n // (`reservationHoldsDates`). Dropping it made every unpaid `pending` row\n // block forever, so the front desk was refused a walk-in into a room that\n // an abandoned online checkout had left empty.\n const existing = reservations\n .filter((item: any) => item.resourceId === walkIn.resourceId)\n .map((item: any) => ({\n checkInDayMs: item.checkInDayMs,\n checkOutDayMs: item.checkOutDayMs,\n status: item.status,\n createdAtMs: item.createdAtMs,\n }))\n if (\n !CommerceModel.isRangeAvailable(resource, existing, checkInDayMs, checkOutDayMs)\n ) {\n return void enqueueSnackbar('Those dates are taken', {\n variant: 'warning',\n persist: false,\n })\n }\n const quote = CommerceModel.computeReservationQuote(\n resource,\n checkInDayMs,\n checkOutDayMs,\n )\n await setDoc(\n doc(\n firestore,\n 'hosts',\n hostId,\n 'reservations',\n Aglyn.createResourceUid(),\n ),\n {\n resourceId: walkIn.resourceId,\n status: 'confirmed',\n checkInDayMs,\n checkOutDayMs,\n guestName: walkIn.guestName || null,\n guestEmail: null,\n nights: quote.nights,\n totalCents: quote.totalCents,\n depositCents: 0,\n paidCents: 0,\n createdAtMs: Date.now(),\n } satisfies CommerceModel.HostReservation,\n )\n setWalkIn(null)\n enqueueSnackbar('Walk-in reserved — collect payment at POS', {\n variant: 'success',\n persist: false,\n })\n }, [walkIn, resourceDocs, reservations, firestore, hostId, enqueueSnackbar])\n\n return (\n <CardDisplay\n header={'Reservations'}\n help={pluginDocsHelp('pos', { anchor: '#reservations' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{'Resources'}</Typography>\n {(resourceDocs ?? []).map((resource: any) => (\n <Stack\n key={resource.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Stack sx={{ flex: 1, minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {`${resource.name} · $${resource.nightlyRateUsd}/night`}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {`id: ${resource.$id}`}\n </Typography>\n </Stack>\n <Button\n size=\"small\"\n onClick={() => setResourceDraft({ id: resource.$id, ...resource })}\n >\n {'Edit'}\n </Button>\n <Button\n size=\"small\"\n color=\"error\"\n onClick={async () => {\n const confirmed = await confirm({\n title: 'Remove this resource?',\n description:\n 'Existing reservations keep their records; the widget ' +\n 'stops accepting new stays.',\n confirmationText: 'Remove',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n await deleteDoc(\n doc(firestore, 'hosts', hostId, 'resources', resource.$id),\n )\n }}\n >\n {'Remove'}\n </Button>\n </Stack>\n ))}\n <Stack direction=\"row\" spacing={1}>\n <Button\n size=\"small\"\n onClick={() =>\n setResourceDraft({\n id: null,\n name: '',\n nightlyRateUsd: 100,\n depositPct: 30,\n minNights: 1,\n })\n }\n >\n {'Add resource'}\n </Button>\n <Button\n size=\"small\"\n disabled={(resourceDocs?.length ?? 0) === 0}\n onClick={() =>\n setWalkIn({\n resourceId: resourceDocs?.[0]?.$id ?? '',\n checkIn: '',\n checkOut: '',\n guestName: '',\n })\n }\n >\n {'Walk-in'}\n </Button>\n </Stack>\n\n <Typography variant=\"subtitle2\" sx={{ mt: 1 }}>\n {'Upcoming & recent stays'}\n </Typography>\n {reservations.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Reservations from the widget (and walk-ins) appear here.'}\n </Typography>\n ) : (\n reservations.map((reservation: any) => (\n <Stack\n key={reservation.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Stack sx={{ flex: 1, minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {`${resourceNames[reservation.resourceId] ?? '?'} · ` +\n `${day(reservation.checkInDayMs)} → ${day(reservation.checkOutDayMs)}`}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {[\n reservation.guestName ?? reservation.guestEmail ?? 'Guest',\n `${usd(reservation.paidCents)} / ${usd(reservation.totalCents)} paid`,\n ].join(' · ')}\n </Typography>\n </Stack>\n <Chip\n label={String(reservation.status).replace('_', ' ')}\n size=\"small\"\n variant=\"outlined\"\n color={STATUS_COLOR[reservation.status] ?? 'default'}\n />\n {reservation.status === 'confirmed' ? (\n <>\n <Button size=\"small\" onClick={handleStatus(reservation, 'checked_in')}>\n {'Check in'}\n </Button>\n <Button\n size=\"small\"\n color=\"error\"\n onClick={handleStatus(reservation, 'no_show')}\n >\n {'No-show'}\n </Button>\n </>\n ) : null}\n {reservation.status === 'checked_in' ? (\n <Button\n size=\"small\"\n onClick={async () => {\n // Folio settlement (AGL-317): surface unpaid room\n // charges before the guest leaves.\n const folio = (reservation.folio ?? []) as Array<{\n amountCents: number\n }>\n const folioCents = folio.reduce(\n (sum, entry) => sum + (entry.amountCents ?? 0),\n 0,\n )\n const balanceCents =\n (reservation.totalCents ?? 0) -\n (reservation.paidCents ?? 0)\n if (folioCents > 0 || balanceCents > 0) {\n const confirmed = await confirm({\n title: 'Check out with open balance?',\n description:\n `${folioCents > 0 ? `Room charges: ${usd(folioCents)} (already recorded as paid POS orders). ` : ''}` +\n `${balanceCents > 0 ? `Stay balance due: ${usd(balanceCents)} — collect at the register.` : ''}`,\n confirmationText: 'Check out',\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n }\n await handleStatus(reservation, 'checked_out')()\n }}\n >\n {'Check out'}\n </Button>\n ) : null}\n {['pending', 'confirmed'].includes(reservation.status) ? (\n <Button\n size=\"small\"\n color=\"error\"\n onClick={handleStatus(reservation, 'cancelled')}\n >\n {'Cancel'}\n </Button>\n ) : null}\n </Stack>\n ))\n )}\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={reservations.length}\n hasMore={hasMore}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n </Stack>\n\n <Dialog\n open={Boolean(resourceDraft)}\n onClose={() => setResourceDraft(null)}\n maxWidth=\"xs\"\n fullWidth\n >\n <DialogTitle>\n {resourceDraft?.id ? 'Edit resource' : 'New resource'}\n </DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <TextField\n label=\"Name\"\n value={resourceDraft?.name ?? ''}\n onChange={(event) =>\n setResourceDraft((prev) =>\n prev ? { ...prev, name: event.target.value } : prev,\n )\n }\n size=\"small\"\n autoFocus\n sx={{ mt: 1 }}\n placeholder=\"Lakeside cabin\"\n />\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Nightly rate ($)\"\n value={resourceDraft?.nightlyRateUsd ?? ''}\n onChange={(event) =>\n setResourceDraft((prev) =>\n prev\n ? {\n ...prev,\n nightlyRateUsd: Number(event.target.value) || 0,\n }\n : prev,\n )\n }\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <TextField\n label=\"Weekend ×\"\n placeholder=\"1\"\n value={resourceDraft?.weekendMultiplier ?? ''}\n onChange={(event) =>\n setResourceDraft((prev) =>\n prev\n ? {\n ...prev,\n weekendMultiplier:\n Number(event.target.value) || undefined,\n }\n : prev,\n )\n }\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n </Stack>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Min nights\"\n value={resourceDraft?.minNights ?? ''}\n onChange={(event) =>\n setResourceDraft((prev) =>\n prev\n ? {\n ...prev,\n minNights:\n Math.round(Number(event.target.value)) || undefined,\n }\n : prev,\n )\n }\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'numeric' } }}\n />\n <TextField\n label=\"Deposit %\"\n placeholder=\"100 = pay in full\"\n value={resourceDraft?.depositPct ?? ''}\n onChange={(event) =>\n setResourceDraft((prev) =>\n prev\n ? {\n ...prev,\n depositPct:\n Math.round(Number(event.target.value)) || undefined,\n }\n : prev,\n )\n }\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'numeric' } }}\n />\n <TextField\n label=\"Free cancel (hrs)\"\n value={resourceDraft?.cancellationHours ?? ''}\n onChange={(event) =>\n setResourceDraft((prev) =>\n prev\n ? {\n ...prev,\n cancellationHours:\n Math.round(Number(event.target.value)) || undefined,\n }\n : prev,\n )\n }\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'numeric' } }}\n />\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setResourceDraft(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={\n !resourceDraft?.name?.trim() ||\n !((resourceDraft?.nightlyRateUsd ?? 0) > 0)\n }\n onClick={handleResourceSave}\n >\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n\n <Dialog\n open={Boolean(walkIn)}\n onClose={() => setWalkIn(null)}\n maxWidth=\"xs\"\n fullWidth\n >\n <DialogTitle>{'Walk-in reservation'}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <TextField\n label=\"Resource\"\n value={walkIn?.resourceId ?? ''}\n onChange={(event) =>\n setWalkIn((prev) =>\n prev ? { ...prev, resourceId: event.target.value } : prev,\n )\n }\n size=\"small\"\n select\n sx={{ mt: 1 }}\n >\n {(resourceDocs ?? []).map((resource: any) => (\n <MenuItem key={resource.$id} value={resource.$id}>\n {resource.name}\n </MenuItem>\n ))}\n </TextField>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Check-in\"\n type=\"date\"\n value={walkIn?.checkIn ?? ''}\n onChange={(event) =>\n setWalkIn((prev) =>\n prev ? { ...prev, checkIn: event.target.value } : prev,\n )\n }\n size=\"small\"\n fullWidth\n slotProps={{ inputLabel: { shrink: true } }}\n />\n <TextField\n label=\"Check-out\"\n type=\"date\"\n value={walkIn?.checkOut ?? ''}\n onChange={(event) =>\n setWalkIn((prev) =>\n prev ? { ...prev, checkOut: event.target.value } : prev,\n )\n }\n size=\"small\"\n fullWidth\n slotProps={{ inputLabel: { shrink: true } }}\n />\n </Stack>\n <TextField\n label=\"Guest name\"\n value={walkIn?.guestName ?? ''}\n onChange={(event) =>\n setWalkIn((prev) =>\n prev ? { ...prev, guestName: event.target.value } : prev,\n )\n }\n size=\"small\"\n />\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setWalkIn(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={!walkIn?.resourceId || !walkIn?.checkIn || !walkIn?.checkOut}\n onClick={handleWalkIn}\n >\n {'Reserve'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nReservationsCard.displayName = 'ReservationsCard'\n\nexport default ReservationsCard\n"],"names":["Aglyn","CommerceModel","CardDisplay","useConfirmationContext","ListPagination","useSnackbar","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","Typography","collection","deleteDoc","doc","limit","orderBy","query","setDoc","updateDoc","useCallback","useMemo","useState","useFirestore","useFirestoreCollection","usePagedCollection","writeGuardedBySeed","pluginDocsHelp","STATUS_COLOR","pending","confirmed","checked_in","checked_out","cancelled","no_show","usd","cents","toFixed","day","dayMs","Date","toISOString","slice","ReservationsCard","props","resourceDraft","hostId","firestore","enqueueSnackbar","confirm","data","resourceDocs","status","resourcesStatus","fromCache","resourcesFromCache","idField","rows","reservations","hasMore","page","setPage","pageSize","setPageSize","pageLimit","resourceNames","map","resource","$id","name","setResourceDraft","walkIn","setWalkIn","handleResourceSave","trim","nightlyRateUsd","seeded","id","_syntheticId","verdict","subject","unreadable","Boolean","createResourceUid","merge","ok","message","variant","persist","handleStatus","reservation","handleWalkIn","resourceId","checkIn","checkOut","find","item","checkInDayMs","parse","checkOutDayMs","existing","filter","createdAtMs","isRangeAvailable","quote","computeReservationQuote","guestName","guestEmail","nights","totalCents","depositCents","paidCents","now","header","help","anchor","contentGutterX","contentGutterY","spacing","direction","sx","alignItems","flex","minWidth","noWrap","color","size","onClick","title","description","confirmationText","confirmationButtonProps","then","catch","depositPct","minNights","disabled","length","mt","join","label","String","replace","folio","folioCents","reduce","sum","entry","amountCents","balanceCents","includes","rowCount","onPageChange","onPageSizeChange","open","onClose","maxWidth","fullWidth","display","flexDirection","gap","value","onChange","event","prev","target","autoFocus","placeholder","Number","slotProps","htmlInput","inputMode","weekendMultiplier","undefined","Math","round","cancellationHours","select","type","inputLabel","shrink","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/reservations-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'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport * as CommerceModel from '../../model'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\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 {\n collection,\n deleteDoc,\n doc,\n getDocs,\n limit,\n orderBy,\n query,\n setDoc,\n updateDoc,\n where,\n} from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport {\n useFirestoreCollection,\n usePagedCollection,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\n\nexport interface ReservationsCardProps {\n hostId: string\n}\n\nconst STATUS_COLOR: Record<\n string,\n 'default' | 'success' | 'warning' | 'error' | 'info'\n> = {\n pending: 'warning',\n confirmed: 'info',\n checked_in: 'success',\n checked_out: 'default',\n cancelled: 'default',\n no_show: 'error',\n}\n\nconst usd = (cents: number | undefined) =>\n `$${((cents ?? 0) / 100).toFixed(2)}`\nconst day = (dayMs: number | undefined) =>\n dayMs ? new Date(dayMs).toISOString().slice(0, 10) : '—'\n\n/**\n * Reservations console (AGL-311): resources CRUD (nightly pricing,\n * seasons via multipliers, deposits) plus the reservations list with\n * check-in/out, no-show, cancel, and walk-in creation. The storefront\n * widget books against the same docs.\n */\nexport function ReservationsCard(props: ReservationsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const {\n data: resourceDocs,\n status: resourcesStatus,\n /**\n * The rows this editor is seeded from are unconfirmed by the server\n * (AGL-1358). Editing spreads a whole stored resource into\n * `resourceDraft` and writes all of it back, so `merge: true` protects\n * nothing. `blocks` is the field that costs more than money: it is the\n * manually blocked date ranges `isRangeAvailable` reads, so a cached seed\n * re-opens dates that were closed and lets the next guest book a room\n * that is already spoken for.\n */\n fromCache: resourcesFromCache,\n } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'resources'), limit(50)),\n [firestore, hostId],\n { idField: '$id' },\n )\n /*\n * Ordered by the server, and a growing window rather than a fixed 300.\n *\n * `limit(300)` carried no `orderBy`, so Firestore returned DOCUMENT-ID\n * order and the client sort below arranged that pseudo-random sample by\n * check-in. The ordering is unchanged — earliest check-in first, exactly\n * what the sort did — it is now the QUERY's, so the window is the real\n * front of the list and the rest can be reached.\n */\n const {\n rows: reservations,\n hasMore,\n page,\n setPage,\n pageSize,\n setPageSize,\n } = usePagedCollection<any>(\n (pageLimit) =>\n query(\n collection(firestore, 'hosts', hostId, 'reservations'),\n orderBy('checkInDayMs', 'asc'),\n limit(pageLimit),\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const resourceNames = useMemo(() => {\n const map: Record<string, string> = {}\n for (const resource of resourceDocs ?? []) {\n map[resource.$id] = resource.name\n }\n return map\n }, [resourceDocs])\n\n const [resourceDraft, setResourceDraft] = useState<\n (Partial<CommerceModel.HostResource> & { id: string | null }) | null\n >(null)\n const [walkIn, setWalkIn] = useState<{\n resourceId: string\n checkIn: string\n checkOut: string\n guestName: string\n } | null>(null)\n\n const handleResourceSave = useCallback(async () => {\n if (!resourceDraft?.name?.trim() || !(resourceDraft.nightlyRateUsd! > 0)) {\n return\n }\n /**\n * `$id` comes off with `id` (AGL-1374).\n *\n * The editor is seeded `{ id: resource.$id, ...resource }`, and that\n * spread carries the listener's SYNTHETIC `$id` — `idField: '$id'`\n * stamps the document id onto the in-memory row, where nothing persists\n * it. Destructuring only `id` left `$id` in `data`, so every save wrote\n * it into the stored document as a real field. Nothing reads it; it is\n * the listener's bookkeeping leaking into storage, and once there no\n * reader can tell it from a field the card meant to write.\n */\n const seeded = resourceDraft as typeof resourceDraft & { $id?: string }\n const { id, $id: _syntheticId, ...data } = seeded\n /**\n * Refuse an EDIT whose seed the server never confirmed (AGL-1358).\n * `data` is the whole stored row spread out of the listener, so\n * `merge: true` has nothing left to protect — every field is present,\n * `blocks` and the seasonal multipliers included.\n *\n * Only the edit path. A NEW resource is built from defaults at a fresh\n * uid and can overwrite nothing, and the first snapshot of any listener\n * is `fromCache: true`, so guarding a create would refuse a save that was\n * never unsafe.\n *\n * The guard WRAPS the write — an early return is a shape you can keep\n * while losing the protection.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'resource',\n unreadable: Boolean(id) && resourcesStatus === 'error',\n fromCache: Boolean(id) && resourcesFromCache,\n },\n async () => {\n await setDoc(\n doc(\n firestore,\n 'hosts',\n hostId,\n 'resources',\n id ?? Aglyn.createResourceUid(),\n ),\n {\n ...data,\n name: resourceDraft.name!.trim().slice(0, 120),\n },\n { merge: true },\n )\n },\n )\n // Before `setResourceDraft(null)`, so a refusal keeps the dialog open\n // with what was typed.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n setResourceDraft(null)\n enqueueSnackbar('Resource saved', { variant: 'success', persist: false })\n }, [\n resourceDraft,\n firestore,\n hostId,\n enqueueSnackbar,\n resourcesStatus,\n resourcesFromCache,\n ])\n\n const handleStatus = useCallback(\n (reservation: any, status: CommerceModel.ReservationStatus) => async () => {\n await updateDoc(\n doc(firestore, 'hosts', hostId, 'reservations', reservation.$id),\n { status },\n )\n },\n [firestore, hostId],\n )\n\n const handleWalkIn = useCallback(async () => {\n if (!walkIn?.resourceId || !walkIn.checkIn || !walkIn.checkOut) return\n const resource = (resourceDocs ?? []).find(\n (item: any) => item.$id === walkIn.resourceId,\n ) as CommerceModel.HostResource | undefined\n if (!resource) return\n const checkInDayMs = Date.parse(`${walkIn.checkIn}T00:00:00Z`)\n const checkOutDayMs = Date.parse(`${walkIn.checkOut}T00:00:00Z`)\n /*\n * Every stay of THIS resource that ends after the walk-in arrives — the\n * same query `reserve.ts` checks a booking against, on the composite it\n * already has (AGL-3321). This used to test the rows on the card's\n * current page, so a stay on the next page did not block the dates and\n * the desk could double-book the room.\n */\n const overlapping = await getDocs(\n query(\n collection(firestore, 'hosts', hostId, 'reservations'),\n where('resourceId', '==', walkIn.resourceId),\n where('checkOutDayMs', '>', checkInDayMs),\n orderBy('checkOutDayMs'),\n limit(500),\n ),\n ).catch(() => null)\n if (!overlapping) {\n return void enqueueSnackbar('Could not check those dates — try again', {\n variant: 'warning',\n persist: false,\n })\n }\n // `createdAtMs` carried through because the pending hold lapses on it\n // (`reservationHoldsDates`). Dropping it made every unpaid `pending` row\n // block forever, so the front desk was refused a walk-in into a room that\n // an abandoned online checkout had left empty.\n const existing = overlapping.docs\n .map((snapshot) => snapshot.data() as any)\n .map((item: any) => ({\n checkInDayMs: item.checkInDayMs,\n checkOutDayMs: item.checkOutDayMs,\n status: item.status,\n createdAtMs: item.createdAtMs,\n }))\n if (\n !CommerceModel.isRangeAvailable(resource, existing, checkInDayMs, checkOutDayMs)\n ) {\n return void enqueueSnackbar('Those dates are taken', {\n variant: 'warning',\n persist: false,\n })\n }\n const quote = CommerceModel.computeReservationQuote(\n resource,\n checkInDayMs,\n checkOutDayMs,\n )\n await setDoc(\n doc(\n firestore,\n 'hosts',\n hostId,\n 'reservations',\n Aglyn.createResourceUid(),\n ),\n {\n resourceId: walkIn.resourceId,\n status: 'confirmed',\n checkInDayMs,\n checkOutDayMs,\n guestName: walkIn.guestName || null,\n guestEmail: null,\n nights: quote.nights,\n totalCents: quote.totalCents,\n depositCents: 0,\n paidCents: 0,\n createdAtMs: Date.now(),\n } satisfies CommerceModel.HostReservation,\n )\n setWalkIn(null)\n enqueueSnackbar('Walk-in reserved — collect payment at POS', {\n variant: 'success',\n persist: false,\n })\n }, [walkIn, resourceDocs, firestore, hostId, enqueueSnackbar])\n\n return (\n <CardDisplay\n header={'Reservations'}\n help={pluginDocsHelp('pos', { anchor: '#reservations' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{'Resources'}</Typography>\n {(resourceDocs ?? []).map((resource: any) => (\n <Stack\n key={resource.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Stack sx={{ flex: 1, minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {`${resource.name} · $${resource.nightlyRateUsd}/night`}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {`id: ${resource.$id}`}\n </Typography>\n </Stack>\n <Button\n size=\"small\"\n onClick={() => setResourceDraft({ id: resource.$id, ...resource })}\n >\n {'Edit'}\n </Button>\n <Button\n size=\"small\"\n color=\"error\"\n onClick={async () => {\n const confirmed = await confirm({\n title: 'Remove this resource?',\n description:\n 'Existing reservations keep their records; the widget ' +\n 'stops accepting new stays.',\n confirmationText: 'Remove',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n await deleteDoc(\n doc(firestore, 'hosts', hostId, 'resources', resource.$id),\n )\n }}\n >\n {'Remove'}\n </Button>\n </Stack>\n ))}\n <Stack direction=\"row\" spacing={1}>\n <Button\n size=\"small\"\n onClick={() =>\n setResourceDraft({\n id: null,\n name: '',\n nightlyRateUsd: 100,\n depositPct: 30,\n minNights: 1,\n })\n }\n >\n {'Add resource'}\n </Button>\n <Button\n size=\"small\"\n disabled={(resourceDocs?.length ?? 0) === 0}\n onClick={() =>\n setWalkIn({\n resourceId: resourceDocs?.[0]?.$id ?? '',\n checkIn: '',\n checkOut: '',\n guestName: '',\n })\n }\n >\n {'Walk-in'}\n </Button>\n </Stack>\n\n <Typography variant=\"subtitle2\" sx={{ mt: 1 }}>\n {'Upcoming & recent stays'}\n </Typography>\n {reservations.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Reservations from the widget (and walk-ins) appear here.'}\n </Typography>\n ) : (\n reservations.map((reservation: any) => (\n <Stack\n key={reservation.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Stack sx={{ flex: 1, minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {`${resourceNames[reservation.resourceId] ?? '?'} · ` +\n `${day(reservation.checkInDayMs)} → ${day(reservation.checkOutDayMs)}`}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {[\n reservation.guestName ?? reservation.guestEmail ?? 'Guest',\n `${usd(reservation.paidCents)} / ${usd(reservation.totalCents)} paid`,\n ].join(' · ')}\n </Typography>\n </Stack>\n <Chip\n label={String(reservation.status).replace('_', ' ')}\n size=\"small\"\n variant=\"outlined\"\n color={STATUS_COLOR[reservation.status] ?? 'default'}\n />\n {reservation.status === 'confirmed' ? (\n <>\n <Button size=\"small\" onClick={handleStatus(reservation, 'checked_in')}>\n {'Check in'}\n </Button>\n <Button\n size=\"small\"\n color=\"error\"\n onClick={handleStatus(reservation, 'no_show')}\n >\n {'No-show'}\n </Button>\n </>\n ) : null}\n {reservation.status === 'checked_in' ? (\n <Button\n size=\"small\"\n onClick={async () => {\n // Folio settlement (AGL-317): surface unpaid room\n // charges before the guest leaves.\n const folio = (reservation.folio ?? []) as Array<{\n amountCents: number\n }>\n const folioCents = folio.reduce(\n (sum, entry) => sum + (entry.amountCents ?? 0),\n 0,\n )\n const balanceCents =\n (reservation.totalCents ?? 0) -\n (reservation.paidCents ?? 0)\n if (folioCents > 0 || balanceCents > 0) {\n const confirmed = await confirm({\n title: 'Check out with open balance?',\n description:\n `${folioCents > 0 ? `Room charges: ${usd(folioCents)} (already recorded as paid POS orders). ` : ''}` +\n `${balanceCents > 0 ? `Stay balance due: ${usd(balanceCents)} — collect at the register.` : ''}`,\n confirmationText: 'Check out',\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n }\n await handleStatus(reservation, 'checked_out')()\n }}\n >\n {'Check out'}\n </Button>\n ) : null}\n {['pending', 'confirmed'].includes(reservation.status) ? (\n <Button\n size=\"small\"\n color=\"error\"\n onClick={handleStatus(reservation, 'cancelled')}\n >\n {'Cancel'}\n </Button>\n ) : null}\n </Stack>\n ))\n )}\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={reservations.length}\n hasMore={hasMore}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n </Stack>\n\n <Dialog\n open={Boolean(resourceDraft)}\n onClose={() => setResourceDraft(null)}\n maxWidth=\"xs\"\n fullWidth\n >\n <DialogTitle>\n {resourceDraft?.id ? 'Edit resource' : 'New resource'}\n </DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <TextField\n label=\"Name\"\n value={resourceDraft?.name ?? ''}\n onChange={(event) =>\n setResourceDraft((prev) =>\n prev ? { ...prev, name: event.target.value } : prev,\n )\n }\n size=\"small\"\n autoFocus\n sx={{ mt: 1 }}\n placeholder=\"Lakeside cabin\"\n />\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Nightly rate ($)\"\n value={resourceDraft?.nightlyRateUsd ?? ''}\n onChange={(event) =>\n setResourceDraft((prev) =>\n prev\n ? {\n ...prev,\n nightlyRateUsd: Number(event.target.value) || 0,\n }\n : prev,\n )\n }\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <TextField\n label=\"Weekend ×\"\n placeholder=\"1\"\n value={resourceDraft?.weekendMultiplier ?? ''}\n onChange={(event) =>\n setResourceDraft((prev) =>\n prev\n ? {\n ...prev,\n weekendMultiplier:\n Number(event.target.value) || undefined,\n }\n : prev,\n )\n }\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n </Stack>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Min nights\"\n value={resourceDraft?.minNights ?? ''}\n onChange={(event) =>\n setResourceDraft((prev) =>\n prev\n ? {\n ...prev,\n minNights:\n Math.round(Number(event.target.value)) || undefined,\n }\n : prev,\n )\n }\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'numeric' } }}\n />\n <TextField\n label=\"Deposit %\"\n placeholder=\"100 = pay in full\"\n value={resourceDraft?.depositPct ?? ''}\n onChange={(event) =>\n setResourceDraft((prev) =>\n prev\n ? {\n ...prev,\n depositPct:\n Math.round(Number(event.target.value)) || undefined,\n }\n : prev,\n )\n }\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'numeric' } }}\n />\n <TextField\n label=\"Free cancel (hrs)\"\n value={resourceDraft?.cancellationHours ?? ''}\n onChange={(event) =>\n setResourceDraft((prev) =>\n prev\n ? {\n ...prev,\n cancellationHours:\n Math.round(Number(event.target.value)) || undefined,\n }\n : prev,\n )\n }\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'numeric' } }}\n />\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setResourceDraft(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={\n !resourceDraft?.name?.trim() ||\n !((resourceDraft?.nightlyRateUsd ?? 0) > 0)\n }\n onClick={handleResourceSave}\n >\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n\n <Dialog\n open={Boolean(walkIn)}\n onClose={() => setWalkIn(null)}\n maxWidth=\"xs\"\n fullWidth\n >\n <DialogTitle>{'Walk-in reservation'}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <TextField\n label=\"Resource\"\n value={walkIn?.resourceId ?? ''}\n onChange={(event) =>\n setWalkIn((prev) =>\n prev ? { ...prev, resourceId: event.target.value } : prev,\n )\n }\n size=\"small\"\n select\n sx={{ mt: 1 }}\n >\n {(resourceDocs ?? []).map((resource: any) => (\n <MenuItem key={resource.$id} value={resource.$id}>\n {resource.name}\n </MenuItem>\n ))}\n </TextField>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Check-in\"\n type=\"date\"\n value={walkIn?.checkIn ?? ''}\n onChange={(event) =>\n setWalkIn((prev) =>\n prev ? { ...prev, checkIn: event.target.value } : prev,\n )\n }\n size=\"small\"\n fullWidth\n slotProps={{ inputLabel: { shrink: true } }}\n />\n <TextField\n label=\"Check-out\"\n type=\"date\"\n value={walkIn?.checkOut ?? ''}\n onChange={(event) =>\n setWalkIn((prev) =>\n prev ? { ...prev, checkOut: event.target.value } : prev,\n )\n }\n size=\"small\"\n fullWidth\n slotProps={{ inputLabel: { shrink: true } }}\n />\n </Stack>\n <TextField\n label=\"Guest name\"\n value={walkIn?.guestName ?? ''}\n onChange={(event) =>\n setWalkIn((prev) =>\n prev ? { ...prev, guestName: event.target.value } : prev,\n )\n }\n size=\"small\"\n />\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setWalkIn(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={!walkIn?.resourceId || !walkIn?.checkIn || !walkIn?.checkOut}\n onClick={handleWalkIn}\n >\n {'Reserve'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nReservationsCard.displayName = 'ReservationsCard'\n\nexport default ReservationsCard\n"],"names":["Aglyn","CommerceModel","CardDisplay","useConfirmationContext","ListPagination","useSnackbar","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","Typography","collection","deleteDoc","doc","getDocs","limit","orderBy","query","setDoc","updateDoc","where","useCallback","useMemo","useState","useFirestore","useFirestoreCollection","usePagedCollection","writeGuardedBySeed","pluginDocsHelp","STATUS_COLOR","pending","confirmed","checked_in","checked_out","cancelled","no_show","usd","cents","toFixed","day","dayMs","Date","toISOString","slice","ReservationsCard","props","resourceDraft","hostId","firestore","enqueueSnackbar","confirm","data","resourceDocs","status","resourcesStatus","fromCache","resourcesFromCache","idField","rows","reservations","hasMore","page","setPage","pageSize","setPageSize","pageLimit","resourceNames","map","resource","$id","name","setResourceDraft","walkIn","setWalkIn","handleResourceSave","trim","nightlyRateUsd","seeded","id","_syntheticId","verdict","subject","unreadable","Boolean","createResourceUid","merge","ok","message","variant","persist","handleStatus","reservation","handleWalkIn","resourceId","checkIn","checkOut","find","item","checkInDayMs","parse","checkOutDayMs","overlapping","catch","existing","docs","snapshot","createdAtMs","isRangeAvailable","quote","computeReservationQuote","guestName","guestEmail","nights","totalCents","depositCents","paidCents","now","header","help","anchor","contentGutterX","contentGutterY","spacing","direction","sx","alignItems","flex","minWidth","noWrap","color","size","onClick","title","description","confirmationText","confirmationButtonProps","then","depositPct","minNights","disabled","length","mt","join","label","String","replace","folio","folioCents","reduce","sum","entry","amountCents","balanceCents","includes","rowCount","onPageChange","onPageSizeChange","open","onClose","maxWidth","fullWidth","display","flexDirection","gap","value","onChange","event","prev","target","autoFocus","placeholder","Number","slotProps","htmlInput","inputMode","weekendMultiplier","undefined","Math","round","cancellationHours","select","type","inputLabel","shrink","displayName"],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { type Firestore } from 'firebase/firestore';
|
|
18
|
+
import * as CommerceModel from '../../model';
|
|
19
|
+
/**
|
|
20
|
+
* Every smart catalog collection on the host, with the rules membership is
|
|
21
|
+
* computed from. Two equalities, which Firestore's single-field indexes
|
|
22
|
+
* answer without a composite; content collections share the path and are
|
|
23
|
+
* excluded by `kind`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function readSmartCollections(firestore: Firestore, hostId: string): Promise<CommerceModel.SmartCollectionRules[]>;
|
|
26
|
+
/**
|
|
27
|
+
* The `collectionIds` field for a product as it is about to be written.
|
|
28
|
+
* Spread it beside the rest of the payload.
|
|
29
|
+
*/
|
|
30
|
+
export declare function productCollectionFields(firestore: Firestore, hostId: string, product: Parameters<typeof CommerceModel.productCollectionIds>[0], smartCollections?: readonly CommerceModel.SmartCollectionRules[]): Promise<{
|
|
31
|
+
collectionIds: string[];
|
|
32
|
+
}>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { collection, getDocs, query, where } from "firebase/firestore";
|
|
17
|
+
import * as CommerceModel from "../../model/index.js";
|
|
18
|
+
/*
|
|
19
|
+
* THE HOST'S SMART COLLECTIONS, AS A PRODUCT WRITER NEEDS THEM (AGL-3321).
|
|
20
|
+
*
|
|
21
|
+
* A product carries `collectionIds` — the smart collections its rules
|
|
22
|
+
* answer — because the storefront reads a smart collection no query can
|
|
23
|
+
* express by that array. So every console path that writes a product's
|
|
24
|
+
* name, tags, categories, variants or type asks the STORE for the host's
|
|
25
|
+
* smart collections at the moment it writes, and stamps the membership
|
|
26
|
+
* `productCollectionIds` computes from them. Asked at write time rather
|
|
27
|
+
* than read off a listener: a listener's rows can be a cached picture, and a
|
|
28
|
+
* membership computed from a stale rule set is the drift this field exists
|
|
29
|
+
* to prevent. When a smart collection itself changes, the
|
|
30
|
+
* `commerce/collection-membership` route re-stamps every product instead.
|
|
31
|
+
*/ /**
|
|
32
|
+
* Every smart catalog collection on the host, with the rules membership is
|
|
33
|
+
* computed from. Two equalities, which Firestore's single-field indexes
|
|
34
|
+
* answer without a composite; content collections share the path and are
|
|
35
|
+
* excluded by `kind`.
|
|
36
|
+
*/ export async function readSmartCollections(firestore, hostId) {
|
|
37
|
+
const snapshot = await getDocs(query(collection(firestore, 'hosts', hostId, 'collections'), where('kind', '==', 'catalog'), where('mode', '==', 'smart')));
|
|
38
|
+
return snapshot.docs.map((entry)=>{
|
|
39
|
+
var _entry_get;
|
|
40
|
+
return {
|
|
41
|
+
id: entry.id,
|
|
42
|
+
rules: (_entry_get = entry.get('rules')) != null ? _entry_get : [],
|
|
43
|
+
matchAll: entry.get('matchAll')
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The `collectionIds` field for a product as it is about to be written.
|
|
49
|
+
* Spread it beside the rest of the payload.
|
|
50
|
+
*/ export async function productCollectionFields(firestore, hostId, product, smartCollections) {
|
|
51
|
+
const rules = smartCollections != null ? smartCollections : await readSmartCollections(firestore, hostId);
|
|
52
|
+
return {
|
|
53
|
+
collectionIds: CommerceModel.productCollectionIds(product, rules)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
//# sourceMappingURL=smart-collections.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/smart-collections.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\nimport {\n collection,\n type Firestore,\n getDocs,\n query,\n where,\n} from 'firebase/firestore'\nimport * as CommerceModel from '../../model'\n\n/*\n * THE HOST'S SMART COLLECTIONS, AS A PRODUCT WRITER NEEDS THEM (AGL-3321).\n *\n * A product carries `collectionIds` — the smart collections its rules\n * answer — because the storefront reads a smart collection no query can\n * express by that array. So every console path that writes a product's\n * name, tags, categories, variants or type asks the STORE for the host's\n * smart collections at the moment it writes, and stamps the membership\n * `productCollectionIds` computes from them. Asked at write time rather\n * than read off a listener: a listener's rows can be a cached picture, and a\n * membership computed from a stale rule set is the drift this field exists\n * to prevent. When a smart collection itself changes, the\n * `commerce/collection-membership` route re-stamps every product instead.\n */\n\n/**\n * Every smart catalog collection on the host, with the rules membership is\n * computed from. Two equalities, which Firestore's single-field indexes\n * answer without a composite; content collections share the path and are\n * excluded by `kind`.\n */\nexport async function readSmartCollections(\n firestore: Firestore,\n hostId: string,\n): Promise<CommerceModel.SmartCollectionRules[]> {\n const snapshot = await getDocs(\n query(\n collection(firestore, 'hosts', hostId, 'collections'),\n where('kind', '==', 'catalog'),\n where('mode', '==', 'smart'),\n ),\n )\n return snapshot.docs.map((entry) => ({\n id: entry.id,\n rules: entry.get('rules') ?? [],\n matchAll: entry.get('matchAll'),\n }))\n}\n\n/**\n * The `collectionIds` field for a product as it is about to be written.\n * Spread it beside the rest of the payload.\n */\nexport async function productCollectionFields(\n firestore: Firestore,\n hostId: string,\n product: Parameters<typeof CommerceModel.productCollectionIds>[0],\n smartCollections?: readonly CommerceModel.SmartCollectionRules[],\n): Promise<{ collectionIds: string[] }> {\n const rules = smartCollections ?? (await readSmartCollections(firestore, hostId))\n return { collectionIds: CommerceModel.productCollectionIds(product, rules) }\n}\n"],"names":["collection","getDocs","query","where","CommerceModel","readSmartCollections","firestore","hostId","snapshot","docs","map","entry","id","rules","get","matchAll","productCollectionFields","product","smartCollections","collectionIds","productCollectionIds"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,UAAU,EAEVC,OAAO,EACPC,KAAK,EACLC,KAAK,QACA,qBAAoB;AAC3B,YAAYC,mBAAmB,uBAAa;AAE5C;;;;;;;;;;;;;CAaC,GAED;;;;;CAKC,GACD,OAAO,eAAeC,qBACpBC,SAAoB,EACpBC,MAAc;IAEd,MAAMC,WAAW,MAAMP,QACrBC,MACEF,WAAWM,WAAW,SAASC,QAAQ,gBACvCJ,MAAM,QAAQ,MAAM,YACpBA,MAAM,QAAQ,MAAM;IAGxB,OAAOK,SAASC,IAAI,CAACC,GAAG,CAAC,CAACC;YAEjBA;eAF4B;YACnCC,IAAID,MAAMC,EAAE;YACZC,KAAK,GAAEF,aAAAA,MAAMG,GAAG,CAAC,oBAAVH,aAAsB,EAAE;YAC/BI,UAAUJ,MAAMG,GAAG,CAAC;QACtB;;AACF;AAEA;;;CAGC,GACD,OAAO,eAAeE,wBACpBV,SAAoB,EACpBC,MAAc,EACdU,OAAiE,EACjEC,gBAAgE;IAEhE,MAAML,QAAQK,2BAAAA,mBAAqB,MAAMb,qBAAqBC,WAAWC;IACzE,OAAO;QAAEY,eAAef,cAAcgB,oBAAoB,CAACH,SAASJ;IAAO;AAC7E"}
|
|
@@ -5,10 +5,10 @@ export interface StockMovementsCardProps {
|
|
|
5
5
|
* Stock movements (AGL-2341) — the adjustment history that had no history
|
|
6
6
|
* view.
|
|
7
7
|
*
|
|
8
|
-
* `hosts/{hostId}/inventoryAdjustments` has
|
|
9
|
-
* "Adjust stock" dialog, the sale
|
|
10
|
-
*
|
|
11
|
-
* calls the collection "adjustment history".
|
|
8
|
+
* `hosts/{hostId}/inventoryAdjustments` has three writers: the products
|
|
9
|
+
* hub's "Adjust stock" dialog, `decrementVariantStock` (the sale, whether the
|
|
10
|
+
* checkout webhook or the POS sold it), and the restock in `cancel-order.ts`.
|
|
11
|
+
* The hub's own comment calls the collection "adjustment history".
|
|
12
12
|
*
|
|
13
13
|
* Its only reader was arithmetic: `cancel-order.ts` projects `appliedDelta`
|
|
14
14
|
* off the `reason: 'sale'` rows to cap what a cancellation may put back. No
|
|
@@ -23,6 +23,15 @@ export interface StockMovementsCardProps {
|
|
|
23
23
|
* zero. They differ rarely and they differ importantly — three sold out of a
|
|
24
24
|
* count of zero is exactly the state a merchant is trying to explain — so
|
|
25
25
|
* both are shown when they disagree and only `delta` when they do not.
|
|
26
|
+
*
|
|
27
|
+
* ## Every filter is on the query (AGL-3321)
|
|
28
|
+
*
|
|
29
|
+
* The ledger used to read its hundred newest movements and filter those, so
|
|
30
|
+
* "Damaged" searched a hundred rows and answered "none" about a ledger full
|
|
31
|
+
* of older ones. Now the Filters panel's clauses are the query's `where`s —
|
|
32
|
+
* see `STOCK_MOVEMENT_QUERY` for what each one costs — and the footer pages
|
|
33
|
+
* the query's answer, so page three of "Damaged" is the third page of damage
|
|
34
|
+
* the ledger holds, however far back it goes.
|
|
26
35
|
*/
|
|
27
36
|
export declare function StockMovementsCard(props: StockMovementsCardProps): import("react").JSX.Element;
|
|
28
37
|
export declare namespace StockMovementsCard {
|