@aglyn/plugins-commerce 1.0.0-beta.246 → 1.0.0-beta.249
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/local-delivery-settings-card.component.js +10 -13
- package/src/lib/components/console/local-delivery-settings-card.component.js.map +1 -1
- package/src/lib/components/console/locations-card.component.js +5 -9
- package/src/lib/components/console/locations-card.component.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/plugins-commerce",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.249",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,18 +25,18 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-ui-next": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-ui-snackstack": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-util-email": "1.0.0-beta.
|
|
34
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
35
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
36
|
-
"@aglyn/shared-util-tools": "1.0.0-beta.
|
|
37
|
-
"@aglyn/tenant-data-admin": "1.0.0-beta.
|
|
38
|
-
"@aglyn/tenant-feature-instance": "1.0.0-beta.
|
|
39
|
-
"@aglyn/tenant-runtime": "1.0.0-beta.
|
|
28
|
+
"@aglyn/aglyn": "1.0.0-beta.249",
|
|
29
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.249",
|
|
30
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.249",
|
|
31
|
+
"@aglyn/shared-ui-next": "1.0.0-beta.249",
|
|
32
|
+
"@aglyn/shared-ui-snackstack": "1.0.0-beta.249",
|
|
33
|
+
"@aglyn/shared-util-email": "1.0.0-beta.249",
|
|
34
|
+
"@aglyn/shared-util-http": "1.0.0-beta.249",
|
|
35
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.249",
|
|
36
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.249",
|
|
37
|
+
"@aglyn/tenant-data-admin": "1.0.0-beta.249",
|
|
38
|
+
"@aglyn/tenant-feature-instance": "1.0.0-beta.249",
|
|
39
|
+
"@aglyn/tenant-runtime": "1.0.0-beta.249",
|
|
40
40
|
"@stripe/react-stripe-js": "^6.12.0",
|
|
41
41
|
"@stripe/stripe-js": "^9.17.0",
|
|
42
42
|
"@swc/helpers": "0.5.23",
|
|
@@ -20,6 +20,7 @@ import * as Aglyn from "@aglyn/aglyn";
|
|
|
20
20
|
import { invalidOpeningHoursLines } from "@aglyn/aglyn/app-utils/local-business";
|
|
21
21
|
import * as CommerceModel from "../../model/index.js";
|
|
22
22
|
import { CardDisplay } from "@aglyn/shared-ui-jsx";
|
|
23
|
+
import { OpeningHoursPicker } from "@aglyn/shared-ui-jsx/components/weekly-hours-picker.component";
|
|
23
24
|
import { useSnackbar } from "@aglyn/shared-ui-snackstack";
|
|
24
25
|
import { Alert, Button, Divider, FormControlLabel, MenuItem, Stack, Switch, TextField, Typography } from "@mui/material";
|
|
25
26
|
import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
|
|
@@ -461,22 +462,18 @@ const LEAD_TIMES = [
|
|
|
461
462
|
]
|
|
462
463
|
}) : null,
|
|
463
464
|
/*#__PURE__*/ _jsx(Divider, {}),
|
|
464
|
-
/*#__PURE__*/ _jsx(
|
|
465
|
-
variant: "subtitle2",
|
|
466
|
-
children: 'Delivery times'
|
|
467
|
-
}),
|
|
468
|
-
/*#__PURE__*/ _jsx(TextField, {
|
|
465
|
+
/*#__PURE__*/ _jsx(OpeningHoursPicker, {
|
|
469
466
|
label: "Delivery windows",
|
|
470
467
|
value: (_current_windows1 = current.windows) != null ? _current_windows1 : '',
|
|
471
|
-
onChange: (
|
|
472
|
-
windows
|
|
468
|
+
onChange: (windows)=>update({
|
|
469
|
+
windows
|
|
473
470
|
}),
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
helperText:
|
|
471
|
+
intervalNoun: "window",
|
|
472
|
+
defaultInterval: {
|
|
473
|
+
start: 9 * 60,
|
|
474
|
+
end: 12 * 60
|
|
475
|
+
},
|
|
476
|
+
helperText: "Switch on each day you deliver, then pick its windows. Each window is offered on its day."
|
|
480
477
|
}),
|
|
481
478
|
/*#__PURE__*/ _jsxs(Stack, {
|
|
482
479
|
direction: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/local-delivery-settings-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 * as Aglyn from '@aglyn/aglyn'\nimport { invalidOpeningHoursLines } from '@aglyn/aglyn/app-utils/local-business'\nimport * as CommerceModel from '../../model'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Button,\n Divider,\n FormControlLabel,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { collection, doc, limit, query, setDoc } from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport {\n useFirestore,\n useFirestoreCollection,\n useFirestoreDoc,\n useUser,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\n\nexport interface LocalDeliverySettingsCardProps {\n hostId: string\n}\n\nconst LEAD_TIMES: ReadonlyArray<[number, string]> = [\n [0, 'No notice'],\n [60, '1 hour'],\n [120, '2 hours'],\n [240, '4 hours'],\n [1440, '1 day'],\n [2880, '2 days'],\n]\n\n/**\n * Local delivery (AGL-3624): the store's OWN delivery — zones by postal code\n * or by distance, each with a fee, a minimum order and a free-over\n * threshold, and the delivery windows a buyer books at the cart. Stored on\n * `settings/store.localDelivery`, written whole so a cleared field is\n * cleared. Marketplace couriers (DoorDash and the rest) are the Delivery apps\n * plugin's, and nothing here touches them.\n *\n * DISTANCE ZONES are offered only where the store's shipping address check\n * can place an address on a map (`GET /api/commerce/local-fulfillment`), and\n * need the store placed first (\"Place on map\"). Postal-code zones need\n * nothing.\n */\nexport function LocalDeliverySettingsCard(props: LocalDeliverySettingsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { data: store, status: storeStatus, fromCache } = useFirestoreDoc<any>(\n () => doc(firestore, 'hosts', hostId, 'settings', 'store'),\n [firestore, hostId],\n )\n const { data: locationDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'locations'), limit(25)),\n [firestore, hostId],\n { idField: '$id' },\n )\n const [draft, setDraft] = useState<CommerceModel.LocalDeliverySettings | null>(null)\n const current: CommerceModel.LocalDeliverySettings = draft ?? (store?.localDelivery as CommerceModel.LocalDeliverySettings) ?? {}\n const update = (patch: Partial<CommerceModel.LocalDeliverySettings>) => setDraft({ ...current, ...patch })\n const [radiusAvailable, setRadiusAvailable] = useState(false)\n useEffect(() => {\n let live = true\n authorizedFetch(user, `/api/commerce/local-fulfillment?hostId=${encodeURIComponent(hostId)}`)\n .then((response) => (response.ok ? response.json() : null))\n .then((answer) => {\n if (live) setRadiusAvailable(Boolean(answer?.radius))\n })\n .catch(() => undefined)\n return () => {\n live = false\n }\n }, [hostId, user])\n\n const zones = current.zones ?? []\n const updateZone = (index: number, patch: Partial<CommerceModel.LocalDeliveryZone> | null) => {\n const next = [...zones]\n if (patch === null) next.splice(index, 1)\n else next[index] = { ...next[index], ...patch }\n update({ zones: next })\n }\n const addZone = () =>\n update({\n zones: [\n ...zones,\n { id: Aglyn.createResourceUid(), name: 'Local delivery', kind: 'postcode', postcodes: [], feeCents: 0 },\n ],\n })\n\n const [locating, setLocating] = useState(false)\n const handleLocate = useCallback(async () => {\n if (!current.locationId) {\n return void enqueueSnackbar('Choose the location deliveries leave from first', { variant: 'info', persist: false })\n }\n setLocating(true)\n try {\n const response = await authorizedFetch(user, '/api/commerce/local-fulfillment', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, action: 'locate', locationId: current.locationId }),\n })\n const payload: any = await response.json().catch(() => ({}))\n if (!response.ok || !payload?.origin) {\n enqueueSnackbar(payload?.error ?? 'Could not place the store on a map', { variant: 'warning', persist: false })\n return\n }\n setDraft({ ...current, origin: payload.origin })\n } finally {\n setLocating(false)\n }\n }, [current, enqueueSnackbar, hostId, user])\n\n const badWindowLines = invalidOpeningHoursLines(current.windows ?? '')\n const problems = CommerceModel.localDeliveryProblems(CommerceModel.normalizeLocalDeliverySettings(current))\n\n const handleSave = useCallback(async () => {\n const value = CommerceModel.normalizeLocalDeliverySettings(current)\n const verdict = await writeGuardedBySeed(\n { subject: 'local delivery settings', unreadable: storeStatus === 'error', fromCache },\n async () => {\n // `mergeFields` replaces `localDelivery` whole and leaves every other\n // map on the settings document alone.\n await setDoc(doc(firestore, 'hosts', hostId, 'settings', 'store'), { localDelivery: value }, {\n mergeFields: ['localDelivery'],\n })\n },\n )\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, { variant: 'warning', persist: false })\n }\n setDraft(null)\n enqueueSnackbar('Local delivery saved', { variant: 'success', persist: false })\n }, [current, enqueueSnackbar, firestore, fromCache, hostId, storeStatus])\n\n const dollars = (cents: number | undefined) => (cents ? String(cents / 100) : '')\n const cents = (raw: string) => (raw.trim() === '' ? 0 : Math.max(0, Math.round(Number(raw) * 100) || 0))\n const locations = [...(locationDocs ?? [])].sort((a: any, b: any) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n\n return (\n <CardDisplay\n header={'Local delivery'}\n help={pluginDocsHelp('pickupAndDelivery', {\n anchor: '#set-up-local-delivery',\n })}\n HeaderProps={{\n action: (\n <Button variant=\"contained\" size=\"small\" disabled={!draft || badWindowLines.length > 0} onClick={handleSave}>\n {'Save'}\n </Button>\n ),\n }}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1.5}>\n <FormControlLabel\n control={\n <Switch\n size=\"small\"\n checked={Boolean(current.enabled)}\n onChange={(event) => update({ enabled: event.target.checked })}\n />\n }\n label=\"Deliver orders yourself\"\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Buyers choose local delivery at the cart, enter their postal code and book a delivery time. ' +\n 'You mark each order out for delivery and delivered, and the buyer is told at each step.'}\n </Typography>\n {problems.length ? (\n <Alert severity=\"warning\">{`Checkout won’t offer delivery yet: ${problems.join(' ')}`}</Alert>\n ) : null}\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={1}>\n <TextField\n label=\"Country\"\n value={current.country ?? ''}\n onChange={(event) => update({ country: event.target.value.toUpperCase().slice(0, 2) })}\n size=\"small\"\n placeholder=\"US\"\n helperText=\"Two letters\"\n sx={{ width: { sm: 120 } }}\n />\n <TextField\n select\n label=\"Leaves from\"\n value={current.locationId ?? ''}\n onChange={(event) => update({ locationId: event.target.value || undefined })}\n size=\"small\"\n sx={{ flex: 1 }}\n helperText=\"Its stock is reserved and sold for delivery orders\"\n >\n <MenuItem value=\"\">{'No particular location'}</MenuItem>\n {locations.map((location: any) => (\n <MenuItem key={location.$id} value={location.$id}>\n {location.name}\n </MenuItem>\n ))}\n </TextField>\n </Stack>\n\n <Divider />\n <Typography variant=\"subtitle2\">{'Zones'}</Typography>\n {zones.map((zone, index) => (\n <Stack key={zone.id} spacing={1}>\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={1}>\n <TextField\n label=\"Zone name\"\n value={zone.name}\n onChange={(event) => updateZone(index, { name: event.target.value })}\n size=\"small\"\n sx={{ flex: 1 }}\n />\n {radiusAvailable || zone.kind === 'radius' ? (\n <TextField\n select\n label=\"Matched by\"\n value={zone.kind}\n onChange={(event) =>\n updateZone(index, { kind: event.target.value as CommerceModel.LocalDeliveryZone['kind'] })\n }\n size=\"small\"\n sx={{ width: { sm: 160 } }}\n >\n <MenuItem value=\"postcode\">{'Postal codes'}</MenuItem>\n <MenuItem value=\"radius\">{'Distance'}</MenuItem>\n </TextField>\n ) : null}\n <Button size=\"small\" color=\"error\" onClick={() => updateZone(index, null)}>\n {'Remove'}\n </Button>\n </Stack>\n {zone.kind === 'radius' ? (\n <TextField\n label=\"Within (km)\"\n type=\"number\"\n value={zone.radiusKm ?? ''}\n onChange={(event) => updateZone(index, { radiusKm: Number(event.target.value) || 0 })}\n size=\"small\"\n sx={{ width: { sm: 160 } }}\n />\n ) : (\n <TextField\n label=\"Postal codes\"\n value={(zone.postcodes ?? []).join(', ')}\n onChange={(event) =>\n updateZone(index, {\n postcodes: event.target.value.split(',').map((code) => code.trim()).filter(Boolean),\n })\n }\n size=\"small\"\n placeholder=\"10001, 100*, 10010-10020\"\n helperText=\"Exact codes, a prefix ending in *, or a range\"\n />\n )}\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={1}>\n <TextField\n label=\"Fee ($)\"\n value={dollars(zone.feeCents)}\n onChange={(event) => updateZone(index, { feeCents: cents(event.target.value) })}\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <TextField\n label=\"Minimum order ($)\"\n value={dollars(zone.minimumCents)}\n onChange={(event) => updateZone(index, { minimumCents: cents(event.target.value) || undefined })}\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <TextField\n label=\"Free over ($)\"\n value={dollars(zone.freeOverCents)}\n onChange={(event) => updateZone(index, { freeOverCents: cents(event.target.value) || undefined })}\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n </Stack>\n <Divider flexItem />\n </Stack>\n ))}\n <Button size=\"small\" sx={{ alignSelf: 'flex-start' }} onClick={addZone}>\n {'Add zone'}\n </Button>\n {zones.some((zone) => zone.kind === 'radius') ? (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\" sx={{ flex: 1 }}>\n {current.origin\n ? `Distances measure from ${current.origin.lat.toFixed(4)}, ${current.origin.lng.toFixed(4)}.`\n : 'Distance zones measure from the location deliveries leave from, once it is placed on a map.'}\n </Typography>\n <Button size=\"small\" disabled={locating} onClick={handleLocate}>\n {current.origin ? 'Place again' : 'Place on map'}\n </Button>\n </Stack>\n ) : null}\n\n <Divider />\n <Typography variant=\"subtitle2\">{'Delivery times'}</Typography>\n <TextField\n label=\"Delivery windows\"\n value={current.windows ?? ''}\n onChange={(event) => update({ windows: event.target.value })}\n multiline\n minRows={2}\n size=\"small\"\n placeholder={'Mo-Fr 09:00-12:00\\nMo-Fr 13:00-17:00'}\n error={badWindowLines.length > 0}\n helperText={\n badWindowLines.length\n ? `Line ${badWindowLines.join(', ')} does not read as days and times, like Mo-Fr 09:00-12:00.`\n : 'One window per line. Each line is offered on each of its days.'\n }\n />\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={1}>\n <TextField\n select\n label=\"Order at least\"\n value={String(current.leadTimeMinutes ?? CommerceModel.LOCAL_DELIVERY_DEFAULT_LEAD_MINUTES)}\n onChange={(event) => update({ leadTimeMinutes: Number(event.target.value) })}\n size=\"small\"\n helperText=\"Before a window starts\"\n sx={{ flex: 1 }}\n >\n {LEAD_TIMES.map(([minutes, label]) => (\n <MenuItem key={minutes} value={String(minutes)}>\n {label}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n label=\"Book up to (days ahead)\"\n type=\"number\"\n value={current.daysAhead ?? CommerceModel.LOCAL_DELIVERY_DEFAULT_DAYS_AHEAD}\n onChange={(event) =>\n update({\n daysAhead: Math.min(\n CommerceModel.LOCAL_DELIVERY_MAX_DAYS_AHEAD,\n Math.max(1, Math.round(Number(event.target.value)) || 1),\n ),\n })\n }\n size=\"small\"\n sx={{ flex: 1 }}\n />\n </Stack>\n <TextField\n label=\"What buyers should know\"\n value={current.instructions ?? ''}\n onChange={(event) => update({ instructions: event.target.value })}\n multiline\n minRows={2}\n size=\"small\"\n placeholder=\"We text when we’re 10 minutes away.\"\n slotProps={{ htmlInput: { maxLength: CommerceModel.PICKUP_INSTRUCTIONS_MAX } }}\n />\n </Stack>\n </CardDisplay>\n )\n}\nLocalDeliverySettingsCard.displayName = 'LocalDeliverySettingsCard'\n\nexport default LocalDeliverySettingsCard\n"],"names":["Aglyn","invalidOpeningHoursLines","CommerceModel","CardDisplay","useSnackbar","Alert","Button","Divider","FormControlLabel","MenuItem","Stack","Switch","TextField","Typography","authorizedFetch","collection","doc","limit","query","setDoc","useCallback","useEffect","useState","useFirestore","useFirestoreCollection","useFirestoreDoc","useUser","writeGuardedBySeed","pluginDocsHelp","LEAD_TIMES","LocalDeliverySettingsCard","props","draft","current","hostId","firestore","data","user","enqueueSnackbar","store","status","storeStatus","fromCache","locationDocs","idField","setDraft","localDelivery","update","patch","radiusAvailable","setRadiusAvailable","live","encodeURIComponent","then","response","ok","json","answer","Boolean","radius","catch","undefined","zones","updateZone","index","next","splice","addZone","id","createResourceUid","name","kind","postcodes","feeCents","locating","setLocating","handleLocate","locationId","variant","persist","method","headers","body","JSON","stringify","action","payload","origin","error","badWindowLines","windows","problems","localDeliveryProblems","normalizeLocalDeliverySettings","handleSave","value","verdict","subject","unreadable","mergeFields","message","dollars","cents","String","raw","trim","Math","max","round","Number","locations","sort","a","b","localeCompare","header","help","anchor","HeaderProps","size","disabled","length","onClick","contentGutterX","contentGutterY","spacing","control","checked","enabled","onChange","event","target","label","color","severity","join","direction","xs","sm","country","toUpperCase","slice","placeholder","helperText","sx","width","select","flex","map","location","$id","zone","type","radiusKm","split","code","filter","slotProps","htmlInput","inputMode","minimumCents","freeOverCents","flexItem","alignSelf","some","alignItems","lat","toFixed","lng","multiline","minRows","leadTimeMinutes","LOCAL_DELIVERY_DEFAULT_LEAD_MINUTES","minutes","daysAhead","LOCAL_DELIVERY_DEFAULT_DAYS_AHEAD","min","LOCAL_DELIVERY_MAX_DAYS_AHEAD","instructions","maxLength","PICKUP_INSTRUCTIONS_MAX","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/local-delivery-settings-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 * as Aglyn from '@aglyn/aglyn'\nimport { invalidOpeningHoursLines } from '@aglyn/aglyn/app-utils/local-business'\nimport * as CommerceModel from '../../model'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { OpeningHoursPicker } from '@aglyn/shared-ui-jsx/components/weekly-hours-picker.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Button,\n Divider,\n FormControlLabel,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { collection, doc, limit, query, setDoc } from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport {\n useFirestore,\n useFirestoreCollection,\n useFirestoreDoc,\n useUser,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\n\nexport interface LocalDeliverySettingsCardProps {\n hostId: string\n}\n\nconst LEAD_TIMES: ReadonlyArray<[number, string]> = [\n [0, 'No notice'],\n [60, '1 hour'],\n [120, '2 hours'],\n [240, '4 hours'],\n [1440, '1 day'],\n [2880, '2 days'],\n]\n\n/**\n * Local delivery (AGL-3624): the store's OWN delivery — zones by postal code\n * or by distance, each with a fee, a minimum order and a free-over\n * threshold, and the delivery windows a buyer books at the cart. Stored on\n * `settings/store.localDelivery`, written whole so a cleared field is\n * cleared. Marketplace couriers (DoorDash and the rest) are the Delivery apps\n * plugin's, and nothing here touches them.\n *\n * DISTANCE ZONES are offered only where the store's shipping address check\n * can place an address on a map (`GET /api/commerce/local-fulfillment`), and\n * need the store placed first (\"Place on map\"). Postal-code zones need\n * nothing.\n */\nexport function LocalDeliverySettingsCard(props: LocalDeliverySettingsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { data: store, status: storeStatus, fromCache } = useFirestoreDoc<any>(\n () => doc(firestore, 'hosts', hostId, 'settings', 'store'),\n [firestore, hostId],\n )\n const { data: locationDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'locations'), limit(25)),\n [firestore, hostId],\n { idField: '$id' },\n )\n const [draft, setDraft] = useState<CommerceModel.LocalDeliverySettings | null>(null)\n const current: CommerceModel.LocalDeliverySettings = draft ?? (store?.localDelivery as CommerceModel.LocalDeliverySettings) ?? {}\n const update = (patch: Partial<CommerceModel.LocalDeliverySettings>) => setDraft({ ...current, ...patch })\n const [radiusAvailable, setRadiusAvailable] = useState(false)\n useEffect(() => {\n let live = true\n authorizedFetch(user, `/api/commerce/local-fulfillment?hostId=${encodeURIComponent(hostId)}`)\n .then((response) => (response.ok ? response.json() : null))\n .then((answer) => {\n if (live) setRadiusAvailable(Boolean(answer?.radius))\n })\n .catch(() => undefined)\n return () => {\n live = false\n }\n }, [hostId, user])\n\n const zones = current.zones ?? []\n const updateZone = (index: number, patch: Partial<CommerceModel.LocalDeliveryZone> | null) => {\n const next = [...zones]\n if (patch === null) next.splice(index, 1)\n else next[index] = { ...next[index], ...patch }\n update({ zones: next })\n }\n const addZone = () =>\n update({\n zones: [\n ...zones,\n { id: Aglyn.createResourceUid(), name: 'Local delivery', kind: 'postcode', postcodes: [], feeCents: 0 },\n ],\n })\n\n const [locating, setLocating] = useState(false)\n const handleLocate = useCallback(async () => {\n if (!current.locationId) {\n return void enqueueSnackbar('Choose the location deliveries leave from first', { variant: 'info', persist: false })\n }\n setLocating(true)\n try {\n const response = await authorizedFetch(user, '/api/commerce/local-fulfillment', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, action: 'locate', locationId: current.locationId }),\n })\n const payload: any = await response.json().catch(() => ({}))\n if (!response.ok || !payload?.origin) {\n enqueueSnackbar(payload?.error ?? 'Could not place the store on a map', { variant: 'warning', persist: false })\n return\n }\n setDraft({ ...current, origin: payload.origin })\n } finally {\n setLocating(false)\n }\n }, [current, enqueueSnackbar, hostId, user])\n\n const badWindowLines = invalidOpeningHoursLines(current.windows ?? '')\n const problems = CommerceModel.localDeliveryProblems(CommerceModel.normalizeLocalDeliverySettings(current))\n\n const handleSave = useCallback(async () => {\n const value = CommerceModel.normalizeLocalDeliverySettings(current)\n const verdict = await writeGuardedBySeed(\n { subject: 'local delivery settings', unreadable: storeStatus === 'error', fromCache },\n async () => {\n // `mergeFields` replaces `localDelivery` whole and leaves every other\n // map on the settings document alone.\n await setDoc(doc(firestore, 'hosts', hostId, 'settings', 'store'), { localDelivery: value }, {\n mergeFields: ['localDelivery'],\n })\n },\n )\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, { variant: 'warning', persist: false })\n }\n setDraft(null)\n enqueueSnackbar('Local delivery saved', { variant: 'success', persist: false })\n }, [current, enqueueSnackbar, firestore, fromCache, hostId, storeStatus])\n\n const dollars = (cents: number | undefined) => (cents ? String(cents / 100) : '')\n const cents = (raw: string) => (raw.trim() === '' ? 0 : Math.max(0, Math.round(Number(raw) * 100) || 0))\n const locations = [...(locationDocs ?? [])].sort((a: any, b: any) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n\n return (\n <CardDisplay\n header={'Local delivery'}\n help={pluginDocsHelp('pickupAndDelivery', {\n anchor: '#set-up-local-delivery',\n })}\n HeaderProps={{\n action: (\n <Button variant=\"contained\" size=\"small\" disabled={!draft || badWindowLines.length > 0} onClick={handleSave}>\n {'Save'}\n </Button>\n ),\n }}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1.5}>\n <FormControlLabel\n control={\n <Switch\n size=\"small\"\n checked={Boolean(current.enabled)}\n onChange={(event) => update({ enabled: event.target.checked })}\n />\n }\n label=\"Deliver orders yourself\"\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Buyers choose local delivery at the cart, enter their postal code and book a delivery time. ' +\n 'You mark each order out for delivery and delivered, and the buyer is told at each step.'}\n </Typography>\n {problems.length ? (\n <Alert severity=\"warning\">{`Checkout won’t offer delivery yet: ${problems.join(' ')}`}</Alert>\n ) : null}\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={1}>\n <TextField\n label=\"Country\"\n value={current.country ?? ''}\n onChange={(event) => update({ country: event.target.value.toUpperCase().slice(0, 2) })}\n size=\"small\"\n placeholder=\"US\"\n helperText=\"Two letters\"\n sx={{ width: { sm: 120 } }}\n />\n <TextField\n select\n label=\"Leaves from\"\n value={current.locationId ?? ''}\n onChange={(event) => update({ locationId: event.target.value || undefined })}\n size=\"small\"\n sx={{ flex: 1 }}\n helperText=\"Its stock is reserved and sold for delivery orders\"\n >\n <MenuItem value=\"\">{'No particular location'}</MenuItem>\n {locations.map((location: any) => (\n <MenuItem key={location.$id} value={location.$id}>\n {location.name}\n </MenuItem>\n ))}\n </TextField>\n </Stack>\n\n <Divider />\n <Typography variant=\"subtitle2\">{'Zones'}</Typography>\n {zones.map((zone, index) => (\n <Stack key={zone.id} spacing={1}>\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={1}>\n <TextField\n label=\"Zone name\"\n value={zone.name}\n onChange={(event) => updateZone(index, { name: event.target.value })}\n size=\"small\"\n sx={{ flex: 1 }}\n />\n {radiusAvailable || zone.kind === 'radius' ? (\n <TextField\n select\n label=\"Matched by\"\n value={zone.kind}\n onChange={(event) =>\n updateZone(index, { kind: event.target.value as CommerceModel.LocalDeliveryZone['kind'] })\n }\n size=\"small\"\n sx={{ width: { sm: 160 } }}\n >\n <MenuItem value=\"postcode\">{'Postal codes'}</MenuItem>\n <MenuItem value=\"radius\">{'Distance'}</MenuItem>\n </TextField>\n ) : null}\n <Button size=\"small\" color=\"error\" onClick={() => updateZone(index, null)}>\n {'Remove'}\n </Button>\n </Stack>\n {zone.kind === 'radius' ? (\n <TextField\n label=\"Within (km)\"\n type=\"number\"\n value={zone.radiusKm ?? ''}\n onChange={(event) => updateZone(index, { radiusKm: Number(event.target.value) || 0 })}\n size=\"small\"\n sx={{ width: { sm: 160 } }}\n />\n ) : (\n <TextField\n label=\"Postal codes\"\n value={(zone.postcodes ?? []).join(', ')}\n onChange={(event) =>\n updateZone(index, {\n postcodes: event.target.value.split(',').map((code) => code.trim()).filter(Boolean),\n })\n }\n size=\"small\"\n placeholder=\"10001, 100*, 10010-10020\"\n helperText=\"Exact codes, a prefix ending in *, or a range\"\n />\n )}\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={1}>\n <TextField\n label=\"Fee ($)\"\n value={dollars(zone.feeCents)}\n onChange={(event) => updateZone(index, { feeCents: cents(event.target.value) })}\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <TextField\n label=\"Minimum order ($)\"\n value={dollars(zone.minimumCents)}\n onChange={(event) => updateZone(index, { minimumCents: cents(event.target.value) || undefined })}\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <TextField\n label=\"Free over ($)\"\n value={dollars(zone.freeOverCents)}\n onChange={(event) => updateZone(index, { freeOverCents: cents(event.target.value) || undefined })}\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n </Stack>\n <Divider flexItem />\n </Stack>\n ))}\n <Button size=\"small\" sx={{ alignSelf: 'flex-start' }} onClick={addZone}>\n {'Add zone'}\n </Button>\n {zones.some((zone) => zone.kind === 'radius') ? (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\" sx={{ flex: 1 }}>\n {current.origin\n ? `Distances measure from ${current.origin.lat.toFixed(4)}, ${current.origin.lng.toFixed(4)}.`\n : 'Distance zones measure from the location deliveries leave from, once it is placed on a map.'}\n </Typography>\n <Button size=\"small\" disabled={locating} onClick={handleLocate}>\n {current.origin ? 'Place again' : 'Place on map'}\n </Button>\n </Stack>\n ) : null}\n\n <Divider />\n <OpeningHoursPicker\n label=\"Delivery windows\"\n value={current.windows ?? ''}\n onChange={(windows) => update({ windows })}\n intervalNoun=\"window\"\n defaultInterval={{ start: 9 * 60, end: 12 * 60 }}\n helperText=\"Switch on each day you deliver, then pick its windows. Each window is offered on its day.\"\n />\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={1}>\n <TextField\n select\n label=\"Order at least\"\n value={String(current.leadTimeMinutes ?? CommerceModel.LOCAL_DELIVERY_DEFAULT_LEAD_MINUTES)}\n onChange={(event) => update({ leadTimeMinutes: Number(event.target.value) })}\n size=\"small\"\n helperText=\"Before a window starts\"\n sx={{ flex: 1 }}\n >\n {LEAD_TIMES.map(([minutes, label]) => (\n <MenuItem key={minutes} value={String(minutes)}>\n {label}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n label=\"Book up to (days ahead)\"\n type=\"number\"\n value={current.daysAhead ?? CommerceModel.LOCAL_DELIVERY_DEFAULT_DAYS_AHEAD}\n onChange={(event) =>\n update({\n daysAhead: Math.min(\n CommerceModel.LOCAL_DELIVERY_MAX_DAYS_AHEAD,\n Math.max(1, Math.round(Number(event.target.value)) || 1),\n ),\n })\n }\n size=\"small\"\n sx={{ flex: 1 }}\n />\n </Stack>\n <TextField\n label=\"What buyers should know\"\n value={current.instructions ?? ''}\n onChange={(event) => update({ instructions: event.target.value })}\n multiline\n minRows={2}\n size=\"small\"\n placeholder=\"We text when we’re 10 minutes away.\"\n slotProps={{ htmlInput: { maxLength: CommerceModel.PICKUP_INSTRUCTIONS_MAX } }}\n />\n </Stack>\n </CardDisplay>\n )\n}\nLocalDeliverySettingsCard.displayName = 'LocalDeliverySettingsCard'\n\nexport default LocalDeliverySettingsCard\n"],"names":["Aglyn","invalidOpeningHoursLines","CommerceModel","CardDisplay","OpeningHoursPicker","useSnackbar","Alert","Button","Divider","FormControlLabel","MenuItem","Stack","Switch","TextField","Typography","authorizedFetch","collection","doc","limit","query","setDoc","useCallback","useEffect","useState","useFirestore","useFirestoreCollection","useFirestoreDoc","useUser","writeGuardedBySeed","pluginDocsHelp","LEAD_TIMES","LocalDeliverySettingsCard","props","draft","current","hostId","firestore","data","user","enqueueSnackbar","store","status","storeStatus","fromCache","locationDocs","idField","setDraft","localDelivery","update","patch","radiusAvailable","setRadiusAvailable","live","encodeURIComponent","then","response","ok","json","answer","Boolean","radius","catch","undefined","zones","updateZone","index","next","splice","addZone","id","createResourceUid","name","kind","postcodes","feeCents","locating","setLocating","handleLocate","locationId","variant","persist","method","headers","body","JSON","stringify","action","payload","origin","error","badWindowLines","windows","problems","localDeliveryProblems","normalizeLocalDeliverySettings","handleSave","value","verdict","subject","unreadable","mergeFields","message","dollars","cents","String","raw","trim","Math","max","round","Number","locations","sort","a","b","localeCompare","header","help","anchor","HeaderProps","size","disabled","length","onClick","contentGutterX","contentGutterY","spacing","control","checked","enabled","onChange","event","target","label","color","severity","join","direction","xs","sm","country","toUpperCase","slice","placeholder","helperText","sx","width","select","flex","map","location","$id","zone","type","radiusKm","split","code","filter","slotProps","htmlInput","inputMode","minimumCents","freeOverCents","flexItem","alignSelf","some","alignItems","lat","toFixed","lng","intervalNoun","defaultInterval","start","end","leadTimeMinutes","LOCAL_DELIVERY_DEFAULT_LEAD_MINUTES","minutes","daysAhead","LOCAL_DELIVERY_DEFAULT_DAYS_AHEAD","min","LOCAL_DELIVERY_MAX_DAYS_AHEAD","instructions","multiline","minRows","maxLength","PICKUP_INSTRUCTIONS_MAX","displayName"],"mappings":"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"}
|
|
@@ -20,6 +20,7 @@ import * as Aglyn from "@aglyn/aglyn";
|
|
|
20
20
|
import * as CommerceModel from "../../model/index.js";
|
|
21
21
|
import { CardDisplay, useConfirmationContext } from "@aglyn/shared-ui-jsx";
|
|
22
22
|
import QuotaReadoutComponent from "@aglyn/shared-ui-jsx/components/quota-readout.component";
|
|
23
|
+
import { OpeningHoursPicker } from "@aglyn/shared-ui-jsx/components/weekly-hours-picker.component";
|
|
23
24
|
import { useSnackbar } from "@aglyn/shared-ui-snackstack";
|
|
24
25
|
import { Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, FormControlLabel, MenuItem, Stack, Switch, TextField, Typography } from "@mui/material";
|
|
25
26
|
import { invalidOpeningHoursLines } from "@aglyn/aglyn/app-utils/local-business";
|
|
@@ -431,18 +432,13 @@ import { pluginDocsHelp } from "@aglyn/aglyn";
|
|
|
431
432
|
color: "warning.main",
|
|
432
433
|
children: 'Add this location’s address so buyers know where to go.'
|
|
433
434
|
}) : null,
|
|
434
|
-
/*#__PURE__*/ _jsx(
|
|
435
|
+
/*#__PURE__*/ _jsx(OpeningHoursPicker, {
|
|
435
436
|
label: "Pickup hours",
|
|
436
437
|
value: (_pickupDraft_hours1 = pickupDraft.hours) != null ? _pickupDraft_hours1 : '',
|
|
437
|
-
onChange: (
|
|
438
|
-
hours
|
|
438
|
+
onChange: (hours)=>setPickupDraft((prior)=>_extends({}, prior, {
|
|
439
|
+
hours
|
|
439
440
|
})),
|
|
440
|
-
|
|
441
|
-
minRows: 2,
|
|
442
|
-
size: "small",
|
|
443
|
-
placeholder: 'Mo-Fr 09:00-17:00\nSa 10:00-14:00',
|
|
444
|
-
error: badHourLines.length > 0,
|
|
445
|
-
helperText: badHourLines.length ? `Line ${badHourLines.join(', ')} does not read as days and times, like Mo-Fr 09:00-17:00.` : 'One line per set of days, like Mo-Fr 09:00-17:00.'
|
|
441
|
+
helperText: "Switch on each day buyers can collect, then pick the times."
|
|
446
442
|
}),
|
|
447
443
|
/*#__PURE__*/ _jsx(TextField, {
|
|
448
444
|
label: "Arrival instructions",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/locations-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 QuotaReadoutComponent from '@aglyn/shared-ui-jsx/components/quota-readout.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n FormControlLabel,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { invalidOpeningHoursLines } from '@aglyn/aglyn/app-utils/local-business'\nimport {\n collection,\n deleteDoc,\n doc,\n getCountFromServer,\n limit,\n query,\n setDoc,\n updateDoc,\n} from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { useFirestoreCollection } from '@aglyn/tenant-feature-instance'\nimport { useHostResourceApi } from '@aglyn/tenant-feature-instance'\nimport { useOrgPlan } from '@aglyn/tenant-feature-instance'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\n\nexport interface LocationsCardProps {\n hostId: string\n}\n\n/**\n * Inventory locations (AGL-286): named stock buckets under\n * `hosts/{hostId}/locations`, capped by the plan's `inventoryLocations`\n * quota (AGL-278). Variant stock splits per location in the products\n * hub; POS registers sell from their location (AGL-312).\n */\nexport function LocationsCard(props: LocationsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const createHostResource = useHostResourceApi()\n const { org, ready: planReady } = useOrgPlan(hostId)\n const { data: locationDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'locations'), limit(25)),\n [firestore, hostId],\n { idField: '$id' },\n )\n const locations = [...(locationDocs ?? [])].sort((a: any, b: any) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n /**\n * The location HEAD-COUNT is a server aggregate, not the length of the\n * capped listener (AGL-1716, the AGL-1706 shape).\n *\n * The smallest margin of the eight, and real for exactly one plan: the\n * listener is `limit(25)` and `inventoryLocations` runs 1 / 1 / 2 / 4 / 6 /\n * 10 / 50, so only Agency's 50 sits above the window. An agency with more\n * than 25 stock buckets read its cap as satisfiable, and\n * `api/hosts/resources` — which counts the collection — refused the add.\n *\n * Re-read after an add AND a delete: locations are hard-deleted here, so\n * unlike the soft-deleting cards a removal really does free a slot on both\n * sides, and a one-shot that missed it would keep refusing.\n *\n * `locations.length === 0` below still decides the FIRST location's\n * `isDefault` flag off the loaded rows, which is the right question for it\n * — that one is about what this card can see, not about the site's total.\n */\n const [locationCountEpoch, setLocationCountEpoch] = useState(0)\n const [serverLocationCount, setServerLocationCount] = useState<number | null>(\n null,\n )\n useEffect(() => {\n let active = true\n void getCountFromServer(collection(firestore, 'hosts', hostId, 'locations'))\n .then((snapshot) => {\n if (active) setServerLocationCount(snapshot.data().count)\n })\n .catch(() => {\n // Falls back to the loaded rows — a LOWER bound and the prior\n // behaviour, never 0, which reads as \"no locations used\" on a site\n // that is over its band.\n })\n return () => {\n active = false\n }\n }, [firestore, hostId, locationCountEpoch])\n const locationCount = serverLocationCount ?? locations.length\n const quota = Aglyn.checkQuota(org, 'inventoryLocations', locationCount)\n const [name, setName] = useState('')\n\n const handleAdd = useCallback(async () => {\n if (!name.trim()) return\n // Held until the org doc lands (AGL-1064): an absent org resolves to\n // the free tier's `inventoryLocations: 1`, so a site that already has\n // its default location would be refused with an upgrade prompt it does\n // not need. The button is disabled too; this guards the race.\n if (!planReady) return\n if (!quota.allowed) {\n return void enqueueSnackbar(\n `Your plan includes ${quota.limit} locations — upgrade for more`,\n { variant: 'info', persist: false },\n )\n }\n try {\n // Creation rides the quota-enforcing resources API (AGL-473).\n await createHostResource({\n hostId,\n resource: 'location',\n data: {\n name: name.trim().slice(0, 80),\n ...(locations.length === 0 ? { isDefault: true } : {}),\n } satisfies CommerceModel.InventoryLocation,\n })\n setName('')\n setLocationCountEpoch((epoch) => epoch + 1)\n } catch (error: any) {\n enqueueSnackbar(error?.message ?? 'Could not add location', {\n variant: 'warning',\n persist: false,\n })\n }\n }, [\n name,\n planReady,\n quota,\n locations.length,\n hostId,\n createHostResource,\n enqueueSnackbar,\n ])\n\n const handleDelete = useCallback(\n (location: any) => async () => {\n const confirmed = await confirm({\n title: 'Remove this location?',\n description:\n `Stock bucketed under \"${location.name}\" folds back into the ` +\n 'flat totals; adjust counts afterward if needed.',\n confirmationText: 'Remove',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n await deleteDoc(doc(firestore, 'hosts', hostId, 'locations', location.$id))\n // A HARD delete, so the slot really is freed on both sides — the\n // one-shot aggregate has to be told (AGL-1716).\n setLocationCountEpoch((epoch) => epoch + 1)\n },\n [confirm, firestore, hostId],\n )\n\n const handleMakeDefault = useCallback(\n (location: any) => async () => {\n for (const other of locations) {\n if (other.isDefault && other.$id !== location.$id) {\n await setDoc(\n doc(firestore, 'hosts', hostId, 'locations', other.$id),\n { isDefault: false },\n { merge: true },\n )\n }\n }\n await setDoc(\n doc(firestore, 'hosts', hostId, 'locations', location.$id),\n { isDefault: true },\n { merge: true },\n )\n },\n [locations, firestore, hostId],\n )\n\n /*\n * A location's postal address (AGL-3612): where parcels can leave from,\n * for anything that ships. Optional, written beside the free-text name.\n */\n const [addressFor, setAddressFor] = useState<any | null>(null)\n const [addressDraft, setAddressDraft] = useState<CommerceModel.PostalAddress>({})\n const openAddress = useCallback(\n (location: any) => () => {\n setAddressFor(location)\n setAddressDraft(CommerceModel.normalizePostalAddress(location.postalAddress) ?? {})\n },\n [],\n )\n const handleSaveAddress = useCallback(async () => {\n if (!addressFor) return\n try {\n await setDoc(\n doc(firestore, 'hosts', hostId, 'locations', addressFor.$id),\n { postalAddress: CommerceModel.normalizePostalAddress(addressDraft) ?? {} },\n { merge: true },\n )\n setAddressFor(null)\n } catch (error: any) {\n enqueueSnackbar(error?.message ?? 'Could not save the address', {\n variant: 'warning',\n persist: false,\n })\n }\n }, [addressDraft, addressFor, enqueueSnackbar, firestore, hostId])\n const addressField = (field: keyof CommerceModel.PostalAddress, label: string) => (\n <TextField\n label={label}\n value={addressDraft[field] ?? ''}\n onChange={(event) =>\n setAddressDraft((prior) => ({\n ...prior,\n [field]: field === 'country' ? event.target.value.toUpperCase().slice(0, 2) : event.target.value,\n }))\n }\n size=\"small\"\n />\n )\n\n /*\n * Pickup at a location (AGL-3624): whether buyers may collect orders here,\n * its hours and what to do on arrival. Checkout offers every location that\n * has it on, and routes the order and its stock there.\n */\n const [pickupFor, setPickupFor] = useState<any | null>(null)\n const [pickupDraft, setPickupDraft] = useState<CommerceModel.PickupLocationSettings>({})\n const openPickup = useCallback(\n (location: any) => () => {\n setPickupFor(location)\n setPickupDraft(CommerceModel.normalizePickupSettings(location.pickup))\n },\n [],\n )\n const badHourLines = invalidOpeningHoursLines(pickupDraft.hours ?? '')\n const handleSavePickup = useCallback(async () => {\n if (!pickupFor) return\n try {\n // `updateDoc` replaces the map whole, so a cleared field is cleared;\n // a merge-set would deep-merge it back.\n await updateDoc(doc(firestore, 'hosts', hostId, 'locations', pickupFor.$id), {\n pickup: CommerceModel.normalizePickupSettings(pickupDraft),\n })\n setPickupFor(null)\n } catch (error: any) {\n enqueueSnackbar(error?.message ?? 'Could not save pickup', {\n variant: 'warning',\n persist: false,\n })\n }\n }, [enqueueSnackbar, firestore, hostId, pickupDraft, pickupFor])\n\n return (\n <CardDisplay\n header={'Inventory locations'}\n help={pluginDocsHelp('commerce', { anchor: '#inventory-locations' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n {locations.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Track stock per warehouse or storefront. Without locations, ' +\n 'every variant has a single stock count.'}\n </Typography>\n ) : (\n locations.map((location: any) => (\n <Stack\n key={location.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Typography variant=\"body2\" sx={{ flex: 1 }} noWrap>\n {location.name}\n </Typography>\n {CommerceModel.normalizePickupSettings(location.pickup).enabled ? (\n <Chip label=\"Pickup\" size=\"small\" color=\"primary\" variant=\"outlined\" />\n ) : null}\n <Button size=\"small\" onClick={openPickup(location)}>\n {'Pickup'}\n </Button>\n <Button size=\"small\" onClick={openAddress(location)}>\n {location.postalAddress?.line1 ? 'Edit address' : 'Add address'}\n </Button>\n {location.isDefault ? (\n <Chip label=\"Default\" size=\"small\" variant=\"outlined\" />\n ) : (\n <Button size=\"small\" onClick={handleMakeDefault(location)}>\n {'Make default'}\n </Button>\n )}\n <Button size=\"small\" color=\"error\" onClick={handleDelete(location)}>\n {'Remove'}\n </Button>\n </Stack>\n ))\n )}\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"New location\"\n value={name}\n onChange={(event) => setName(event.target.value)}\n size=\"small\"\n sx={{ flex: 1 }}\n placeholder=\"Main warehouse\"\n />\n <Button\n size=\"small\"\n disabled={!name.trim() || !planReady}\n onClick={handleAdd}\n >\n {'Add'}\n </Button>\n </Stack>\n {/* The site's locations, not this card's rows (AGL-1716) — the\n readout and the gate must agree, and the gate now counts.\n The wording moved to the shared component in AGL-2113 so the five\n sibling cards that were missing it render the identical string\n rather than five near-misses; the output here is unchanged. */}\n <QuotaReadoutComponent\n ready={planReady}\n used={locationCount}\n limit={quota.limit}\n noun=\"location\"\n />\n </Stack>\n <Dialog open={Boolean(addressFor)} onClose={() => setAddressFor(null)} fullWidth maxWidth=\"sm\">\n <DialogTitle>{`Address of ${addressFor?.name ?? 'this location'}`}</DialogTitle>\n <DialogContent>\n <Stack spacing={1.5} sx={{ pt: 1 }}>\n {addressField('line1', 'Street address')}\n {addressField('line2', 'Apartment, suite (optional)')}\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={1.5}>\n {addressField('city', 'City')}\n {addressField('state', 'State or region')}\n {addressField('postalCode', 'Postal code')}\n {addressField('country', 'Country (two letters)')}\n </Stack>\n {addressField('phone', 'Phone')}\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setAddressFor(null)}>{'Cancel'}</Button>\n <Button variant=\"contained\" onClick={handleSaveAddress}>\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n <Dialog open={Boolean(pickupFor)} onClose={() => setPickupFor(null)} fullWidth maxWidth=\"sm\">\n <DialogTitle>{`Pickup at ${pickupFor?.name ?? 'this location'}`}</DialogTitle>\n <DialogContent>\n <Stack spacing={1.5} sx={{ pt: 1 }}>\n <FormControlLabel\n control={\n <Switch\n checked={Boolean(pickupDraft.enabled)}\n onChange={(event) =>\n setPickupDraft((prior) => ({ ...prior, enabled: event.target.checked }))\n }\n />\n }\n label=\"Buyers can pick up orders here\"\n />\n {pickupDraft.enabled && !pickupFor?.postalAddress?.line1 && !pickupFor?.address ? (\n <Typography variant=\"caption\" color=\"warning.main\">\n {'Add this location’s address so buyers know where to go.'}\n </Typography>\n ) : null}\n <TextField\n label=\"Pickup hours\"\n value={pickupDraft.hours ?? ''}\n onChange={(event) => setPickupDraft((prior) => ({ ...prior, hours: event.target.value }))}\n multiline\n minRows={2}\n size=\"small\"\n placeholder={'Mo-Fr 09:00-17:00\\nSa 10:00-14:00'}\n error={badHourLines.length > 0}\n helperText={\n badHourLines.length\n ? `Line ${badHourLines.join(', ')} does not read as days and times, like Mo-Fr 09:00-17:00.`\n : 'One line per set of days, like Mo-Fr 09:00-17:00.'\n }\n />\n <TextField\n label=\"Arrival instructions\"\n value={pickupDraft.instructions ?? ''}\n onChange={(event) =>\n setPickupDraft((prior) => ({ ...prior, instructions: event.target.value }))\n }\n multiline\n minRows={2}\n size=\"small\"\n placeholder=\"Ring the bell at the side door and give your order number.\"\n slotProps={{ htmlInput: { maxLength: CommerceModel.PICKUP_INSTRUCTIONS_MAX } }}\n />\n <TextField\n select\n label=\"Usually ready in\"\n value={String(pickupDraft.readyWithinMinutes ?? '')}\n onChange={(event) =>\n setPickupDraft((prior) => ({\n ...prior,\n readyWithinMinutes: event.target.value ? Number(event.target.value) : undefined,\n }))\n }\n size=\"small\"\n >\n <MenuItem value=\"\">{'Don’t say'}</MenuItem>\n {[\n [30, '30 minutes'],\n [60, '1 hour'],\n [120, '2 hours'],\n [240, '4 hours'],\n [1440, '1 day'],\n [2880, '2 days'],\n ].map(([minutes, label]) => (\n <MenuItem key={minutes} value={String(minutes)}>\n {label}\n </MenuItem>\n ))}\n </TextField>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Buyers choose this location at checkout. The order’s stock comes off this location, ' +\n 'and the buyer is emailed when you mark the order ready.'}\n </Typography>\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setPickupFor(null)}>{'Cancel'}</Button>\n <Button variant=\"contained\" disabled={badHourLines.length > 0} onClick={handleSavePickup}>\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nLocationsCard.displayName = 'LocationsCard'\n\nexport default LocationsCard\n"],"names":["Aglyn","CommerceModel","CardDisplay","useConfirmationContext","QuotaReadoutComponent","useSnackbar","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","FormControlLabel","MenuItem","Stack","Switch","TextField","Typography","invalidOpeningHoursLines","collection","deleteDoc","doc","getCountFromServer","limit","query","setDoc","updateDoc","useCallback","useEffect","useState","useFirestore","useFirestoreCollection","useHostResourceApi","useOrgPlan","pluginDocsHelp","LocationsCard","props","pickupDraft","pickupFor","hostId","firestore","enqueueSnackbar","confirm","createHostResource","org","ready","planReady","data","locationDocs","idField","locations","sort","a","b","String","name","localeCompare","locationCountEpoch","setLocationCountEpoch","serverLocationCount","setServerLocationCount","active","then","snapshot","count","catch","locationCount","length","quota","checkQuota","setName","handleAdd","trim","allowed","variant","persist","resource","slice","isDefault","epoch","error","message","handleDelete","location","confirmed","title","description","confirmationText","confirmationButtonProps","color","$id","handleMakeDefault","other","merge","addressFor","setAddressFor","addressDraft","setAddressDraft","openAddress","normalizePostalAddress","postalAddress","handleSaveAddress","addressField","field","label","value","onChange","event","prior","target","toUpperCase","size","setPickupFor","setPickupDraft","openPickup","normalizePickupSettings","pickup","badHourLines","hours","handleSavePickup","header","help","anchor","contentGutterX","contentGutterY","spacing","map","direction","sx","alignItems","flex","noWrap","enabled","onClick","line1","placeholder","disabled","used","noun","open","Boolean","onClose","fullWidth","maxWidth","pt","xs","sm","control","checked","address","multiline","minRows","helperText","join","instructions","slotProps","htmlInput","maxLength","PICKUP_INSTRUCTIONS_MAX","select","readyWithinMinutes","Number","undefined","minutes","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/locations-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 QuotaReadoutComponent from '@aglyn/shared-ui-jsx/components/quota-readout.component'\nimport { OpeningHoursPicker } from '@aglyn/shared-ui-jsx/components/weekly-hours-picker.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n FormControlLabel,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { invalidOpeningHoursLines } from '@aglyn/aglyn/app-utils/local-business'\nimport {\n collection,\n deleteDoc,\n doc,\n getCountFromServer,\n limit,\n query,\n setDoc,\n updateDoc,\n} from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { useFirestoreCollection } from '@aglyn/tenant-feature-instance'\nimport { useHostResourceApi } from '@aglyn/tenant-feature-instance'\nimport { useOrgPlan } from '@aglyn/tenant-feature-instance'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\n\nexport interface LocationsCardProps {\n hostId: string\n}\n\n/**\n * Inventory locations (AGL-286): named stock buckets under\n * `hosts/{hostId}/locations`, capped by the plan's `inventoryLocations`\n * quota (AGL-278). Variant stock splits per location in the products\n * hub; POS registers sell from their location (AGL-312).\n */\nexport function LocationsCard(props: LocationsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const createHostResource = useHostResourceApi()\n const { org, ready: planReady } = useOrgPlan(hostId)\n const { data: locationDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'locations'), limit(25)),\n [firestore, hostId],\n { idField: '$id' },\n )\n const locations = [...(locationDocs ?? [])].sort((a: any, b: any) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n /**\n * The location HEAD-COUNT is a server aggregate, not the length of the\n * capped listener (AGL-1716, the AGL-1706 shape).\n *\n * The smallest margin of the eight, and real for exactly one plan: the\n * listener is `limit(25)` and `inventoryLocations` runs 1 / 1 / 2 / 4 / 6 /\n * 10 / 50, so only Agency's 50 sits above the window. An agency with more\n * than 25 stock buckets read its cap as satisfiable, and\n * `api/hosts/resources` — which counts the collection — refused the add.\n *\n * Re-read after an add AND a delete: locations are hard-deleted here, so\n * unlike the soft-deleting cards a removal really does free a slot on both\n * sides, and a one-shot that missed it would keep refusing.\n *\n * `locations.length === 0` below still decides the FIRST location's\n * `isDefault` flag off the loaded rows, which is the right question for it\n * — that one is about what this card can see, not about the site's total.\n */\n const [locationCountEpoch, setLocationCountEpoch] = useState(0)\n const [serverLocationCount, setServerLocationCount] = useState<number | null>(\n null,\n )\n useEffect(() => {\n let active = true\n void getCountFromServer(collection(firestore, 'hosts', hostId, 'locations'))\n .then((snapshot) => {\n if (active) setServerLocationCount(snapshot.data().count)\n })\n .catch(() => {\n // Falls back to the loaded rows — a LOWER bound and the prior\n // behaviour, never 0, which reads as \"no locations used\" on a site\n // that is over its band.\n })\n return () => {\n active = false\n }\n }, [firestore, hostId, locationCountEpoch])\n const locationCount = serverLocationCount ?? locations.length\n const quota = Aglyn.checkQuota(org, 'inventoryLocations', locationCount)\n const [name, setName] = useState('')\n\n const handleAdd = useCallback(async () => {\n if (!name.trim()) return\n // Held until the org doc lands (AGL-1064): an absent org resolves to\n // the free tier's `inventoryLocations: 1`, so a site that already has\n // its default location would be refused with an upgrade prompt it does\n // not need. The button is disabled too; this guards the race.\n if (!planReady) return\n if (!quota.allowed) {\n return void enqueueSnackbar(\n `Your plan includes ${quota.limit} locations — upgrade for more`,\n { variant: 'info', persist: false },\n )\n }\n try {\n // Creation rides the quota-enforcing resources API (AGL-473).\n await createHostResource({\n hostId,\n resource: 'location',\n data: {\n name: name.trim().slice(0, 80),\n ...(locations.length === 0 ? { isDefault: true } : {}),\n } satisfies CommerceModel.InventoryLocation,\n })\n setName('')\n setLocationCountEpoch((epoch) => epoch + 1)\n } catch (error: any) {\n enqueueSnackbar(error?.message ?? 'Could not add location', {\n variant: 'warning',\n persist: false,\n })\n }\n }, [\n name,\n planReady,\n quota,\n locations.length,\n hostId,\n createHostResource,\n enqueueSnackbar,\n ])\n\n const handleDelete = useCallback(\n (location: any) => async () => {\n const confirmed = await confirm({\n title: 'Remove this location?',\n description:\n `Stock bucketed under \"${location.name}\" folds back into the ` +\n 'flat totals; adjust counts afterward if needed.',\n confirmationText: 'Remove',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n await deleteDoc(doc(firestore, 'hosts', hostId, 'locations', location.$id))\n // A HARD delete, so the slot really is freed on both sides — the\n // one-shot aggregate has to be told (AGL-1716).\n setLocationCountEpoch((epoch) => epoch + 1)\n },\n [confirm, firestore, hostId],\n )\n\n const handleMakeDefault = useCallback(\n (location: any) => async () => {\n for (const other of locations) {\n if (other.isDefault && other.$id !== location.$id) {\n await setDoc(\n doc(firestore, 'hosts', hostId, 'locations', other.$id),\n { isDefault: false },\n { merge: true },\n )\n }\n }\n await setDoc(\n doc(firestore, 'hosts', hostId, 'locations', location.$id),\n { isDefault: true },\n { merge: true },\n )\n },\n [locations, firestore, hostId],\n )\n\n /*\n * A location's postal address (AGL-3612): where parcels can leave from,\n * for anything that ships. Optional, written beside the free-text name.\n */\n const [addressFor, setAddressFor] = useState<any | null>(null)\n const [addressDraft, setAddressDraft] = useState<CommerceModel.PostalAddress>({})\n const openAddress = useCallback(\n (location: any) => () => {\n setAddressFor(location)\n setAddressDraft(CommerceModel.normalizePostalAddress(location.postalAddress) ?? {})\n },\n [],\n )\n const handleSaveAddress = useCallback(async () => {\n if (!addressFor) return\n try {\n await setDoc(\n doc(firestore, 'hosts', hostId, 'locations', addressFor.$id),\n { postalAddress: CommerceModel.normalizePostalAddress(addressDraft) ?? {} },\n { merge: true },\n )\n setAddressFor(null)\n } catch (error: any) {\n enqueueSnackbar(error?.message ?? 'Could not save the address', {\n variant: 'warning',\n persist: false,\n })\n }\n }, [addressDraft, addressFor, enqueueSnackbar, firestore, hostId])\n const addressField = (field: keyof CommerceModel.PostalAddress, label: string) => (\n <TextField\n label={label}\n value={addressDraft[field] ?? ''}\n onChange={(event) =>\n setAddressDraft((prior) => ({\n ...prior,\n [field]: field === 'country' ? event.target.value.toUpperCase().slice(0, 2) : event.target.value,\n }))\n }\n size=\"small\"\n />\n )\n\n /*\n * Pickup at a location (AGL-3624): whether buyers may collect orders here,\n * its hours and what to do on arrival. Checkout offers every location that\n * has it on, and routes the order and its stock there.\n */\n const [pickupFor, setPickupFor] = useState<any | null>(null)\n const [pickupDraft, setPickupDraft] = useState<CommerceModel.PickupLocationSettings>({})\n const openPickup = useCallback(\n (location: any) => () => {\n setPickupFor(location)\n setPickupDraft(CommerceModel.normalizePickupSettings(location.pickup))\n },\n [],\n )\n const badHourLines = invalidOpeningHoursLines(pickupDraft.hours ?? '')\n const handleSavePickup = useCallback(async () => {\n if (!pickupFor) return\n try {\n // `updateDoc` replaces the map whole, so a cleared field is cleared;\n // a merge-set would deep-merge it back.\n await updateDoc(doc(firestore, 'hosts', hostId, 'locations', pickupFor.$id), {\n pickup: CommerceModel.normalizePickupSettings(pickupDraft),\n })\n setPickupFor(null)\n } catch (error: any) {\n enqueueSnackbar(error?.message ?? 'Could not save pickup', {\n variant: 'warning',\n persist: false,\n })\n }\n }, [enqueueSnackbar, firestore, hostId, pickupDraft, pickupFor])\n\n return (\n <CardDisplay\n header={'Inventory locations'}\n help={pluginDocsHelp('commerce', { anchor: '#inventory-locations' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n {locations.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Track stock per warehouse or storefront. Without locations, ' +\n 'every variant has a single stock count.'}\n </Typography>\n ) : (\n locations.map((location: any) => (\n <Stack\n key={location.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Typography variant=\"body2\" sx={{ flex: 1 }} noWrap>\n {location.name}\n </Typography>\n {CommerceModel.normalizePickupSettings(location.pickup).enabled ? (\n <Chip label=\"Pickup\" size=\"small\" color=\"primary\" variant=\"outlined\" />\n ) : null}\n <Button size=\"small\" onClick={openPickup(location)}>\n {'Pickup'}\n </Button>\n <Button size=\"small\" onClick={openAddress(location)}>\n {location.postalAddress?.line1 ? 'Edit address' : 'Add address'}\n </Button>\n {location.isDefault ? (\n <Chip label=\"Default\" size=\"small\" variant=\"outlined\" />\n ) : (\n <Button size=\"small\" onClick={handleMakeDefault(location)}>\n {'Make default'}\n </Button>\n )}\n <Button size=\"small\" color=\"error\" onClick={handleDelete(location)}>\n {'Remove'}\n </Button>\n </Stack>\n ))\n )}\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"New location\"\n value={name}\n onChange={(event) => setName(event.target.value)}\n size=\"small\"\n sx={{ flex: 1 }}\n placeholder=\"Main warehouse\"\n />\n <Button\n size=\"small\"\n disabled={!name.trim() || !planReady}\n onClick={handleAdd}\n >\n {'Add'}\n </Button>\n </Stack>\n {/* The site's locations, not this card's rows (AGL-1716) — the\n readout and the gate must agree, and the gate now counts.\n The wording moved to the shared component in AGL-2113 so the five\n sibling cards that were missing it render the identical string\n rather than five near-misses; the output here is unchanged. */}\n <QuotaReadoutComponent\n ready={planReady}\n used={locationCount}\n limit={quota.limit}\n noun=\"location\"\n />\n </Stack>\n <Dialog open={Boolean(addressFor)} onClose={() => setAddressFor(null)} fullWidth maxWidth=\"sm\">\n <DialogTitle>{`Address of ${addressFor?.name ?? 'this location'}`}</DialogTitle>\n <DialogContent>\n <Stack spacing={1.5} sx={{ pt: 1 }}>\n {addressField('line1', 'Street address')}\n {addressField('line2', 'Apartment, suite (optional)')}\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={1.5}>\n {addressField('city', 'City')}\n {addressField('state', 'State or region')}\n {addressField('postalCode', 'Postal code')}\n {addressField('country', 'Country (two letters)')}\n </Stack>\n {addressField('phone', 'Phone')}\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setAddressFor(null)}>{'Cancel'}</Button>\n <Button variant=\"contained\" onClick={handleSaveAddress}>\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n <Dialog open={Boolean(pickupFor)} onClose={() => setPickupFor(null)} fullWidth maxWidth=\"sm\">\n <DialogTitle>{`Pickup at ${pickupFor?.name ?? 'this location'}`}</DialogTitle>\n <DialogContent>\n <Stack spacing={1.5} sx={{ pt: 1 }}>\n <FormControlLabel\n control={\n <Switch\n checked={Boolean(pickupDraft.enabled)}\n onChange={(event) =>\n setPickupDraft((prior) => ({ ...prior, enabled: event.target.checked }))\n }\n />\n }\n label=\"Buyers can pick up orders here\"\n />\n {pickupDraft.enabled && !pickupFor?.postalAddress?.line1 && !pickupFor?.address ? (\n <Typography variant=\"caption\" color=\"warning.main\">\n {'Add this location’s address so buyers know where to go.'}\n </Typography>\n ) : null}\n <OpeningHoursPicker\n label=\"Pickup hours\"\n value={pickupDraft.hours ?? ''}\n onChange={(hours) => setPickupDraft((prior) => ({ ...prior, hours }))}\n helperText=\"Switch on each day buyers can collect, then pick the times.\"\n />\n <TextField\n label=\"Arrival instructions\"\n value={pickupDraft.instructions ?? ''}\n onChange={(event) =>\n setPickupDraft((prior) => ({ ...prior, instructions: event.target.value }))\n }\n multiline\n minRows={2}\n size=\"small\"\n placeholder=\"Ring the bell at the side door and give your order number.\"\n slotProps={{ htmlInput: { maxLength: CommerceModel.PICKUP_INSTRUCTIONS_MAX } }}\n />\n <TextField\n select\n label=\"Usually ready in\"\n value={String(pickupDraft.readyWithinMinutes ?? '')}\n onChange={(event) =>\n setPickupDraft((prior) => ({\n ...prior,\n readyWithinMinutes: event.target.value ? Number(event.target.value) : undefined,\n }))\n }\n size=\"small\"\n >\n <MenuItem value=\"\">{'Don’t say'}</MenuItem>\n {[\n [30, '30 minutes'],\n [60, '1 hour'],\n [120, '2 hours'],\n [240, '4 hours'],\n [1440, '1 day'],\n [2880, '2 days'],\n ].map(([minutes, label]) => (\n <MenuItem key={minutes} value={String(minutes)}>\n {label}\n </MenuItem>\n ))}\n </TextField>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Buyers choose this location at checkout. The order’s stock comes off this location, ' +\n 'and the buyer is emailed when you mark the order ready.'}\n </Typography>\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setPickupFor(null)}>{'Cancel'}</Button>\n <Button variant=\"contained\" disabled={badHourLines.length > 0} onClick={handleSavePickup}>\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nLocationsCard.displayName = 'LocationsCard'\n\nexport default LocationsCard\n"],"names":["Aglyn","CommerceModel","CardDisplay","useConfirmationContext","QuotaReadoutComponent","OpeningHoursPicker","useSnackbar","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","FormControlLabel","MenuItem","Stack","Switch","TextField","Typography","invalidOpeningHoursLines","collection","deleteDoc","doc","getCountFromServer","limit","query","setDoc","updateDoc","useCallback","useEffect","useState","useFirestore","useFirestoreCollection","useHostResourceApi","useOrgPlan","pluginDocsHelp","LocationsCard","props","pickupDraft","pickupFor","hostId","firestore","enqueueSnackbar","confirm","createHostResource","org","ready","planReady","data","locationDocs","idField","locations","sort","a","b","String","name","localeCompare","locationCountEpoch","setLocationCountEpoch","serverLocationCount","setServerLocationCount","active","then","snapshot","count","catch","locationCount","length","quota","checkQuota","setName","handleAdd","trim","allowed","variant","persist","resource","slice","isDefault","epoch","error","message","handleDelete","location","confirmed","title","description","confirmationText","confirmationButtonProps","color","$id","handleMakeDefault","other","merge","addressFor","setAddressFor","addressDraft","setAddressDraft","openAddress","normalizePostalAddress","postalAddress","handleSaveAddress","addressField","field","label","value","onChange","event","prior","target","toUpperCase","size","setPickupFor","setPickupDraft","openPickup","normalizePickupSettings","pickup","badHourLines","hours","handleSavePickup","header","help","anchor","contentGutterX","contentGutterY","spacing","map","direction","sx","alignItems","flex","noWrap","enabled","onClick","line1","placeholder","disabled","used","noun","open","Boolean","onClose","fullWidth","maxWidth","pt","xs","sm","control","checked","address","helperText","instructions","multiline","minRows","slotProps","htmlInput","maxLength","PICKUP_INSTRUCTIONS_MAX","select","readyWithinMinutes","Number","undefined","minutes","displayName"],"mappings":"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"}
|