astro-tractstack 2.3.1 → 2.3.3
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/bin/create-tractstack.js +3 -3
- package/dist/index.js +69 -11
- package/package.json +1 -1
- package/templates/custom/shopify/Cart.tsx +99 -19
- package/templates/custom/shopify/CheckoutModal.tsx +196 -10
- package/templates/custom/shopify/ShopifyCartManager.tsx +79 -76
- package/templates/custom/shopify/ShopifyCheckout.tsx +4 -33
- package/templates/custom/shopify/ShopifyProductGrid.tsx +42 -14
- package/templates/custom/shopify/ShopifyServiceList.tsx +94 -50
- package/templates/custom/shopify/cart.astro +7 -1
- package/templates/src/components/Footer.astro +2 -2
- package/templates/src/components/Header.astro +17 -9
- package/templates/src/components/Menu.tsx +157 -135
- package/templates/src/components/codehooks/BunnyVideoSetup.tsx +2 -2
- package/templates/src/components/codehooks/EpinetDurationSelector.tsx +27 -6
- package/templates/src/components/codehooks/EpinetTableView.tsx +153 -112
- package/templates/src/components/codehooks/EpinetWrapper.tsx +4 -1
- package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +8 -1
- package/templates/src/components/codehooks/ProductCardSetup.tsx +9 -1
- package/templates/src/components/codehooks/ProductGridSetup.tsx +9 -1
- package/templates/src/components/compositor/nodes/BgPaneWrapper.tsx +2 -1
- package/templates/src/components/compositor/nodes/GhostInsertBlock.tsx +1 -1
- package/templates/src/components/edit/ToolBar.tsx +2 -1
- package/templates/src/components/edit/context/ContextPaneConfig_slug.tsx +2 -2
- package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +13 -0
- package/templates/src/components/edit/pane/AddPanePanel_newCustomCopy.tsx +2 -2
- package/templates/src/components/edit/pane/ConfigPanePanel.tsx +1 -1
- package/templates/src/components/edit/state/SaveModal.tsx +1 -1
- package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +8 -3
- package/templates/src/components/form/DateTimeInput.tsx +10 -3
- package/templates/src/components/form/FileUpload.tsx +11 -5
- package/templates/src/components/form/NumberInput.tsx +2 -2
- package/templates/src/components/form/advanced/APIConfigSection.tsx +221 -39
- package/templates/src/components/form/brand/SiteConfigSection.tsx +10 -0
- package/templates/src/components/form/shopify/SchedulingSection.tsx +44 -0
- package/templates/src/components/storykeep/Dashboard_Advanced.tsx +10 -1
- package/templates/src/components/storykeep/Dashboard_Shopify.tsx +16 -8
- package/templates/src/components/storykeep/controls/content/BeliefForm.tsx +2 -2
- package/templates/src/components/storykeep/controls/content/ManageContent.tsx +1 -0
- package/templates/src/components/storykeep/controls/content/ProductTable.tsx +2 -2
- package/templates/src/components/storykeep/controls/content/ResourceBulkIngest.tsx +79 -51
- package/templates/src/components/storykeep/controls/content/ResourceForm.tsx +80 -0
- package/templates/src/components/storykeep/controls/content/ResourceTable.tsx +1 -0
- package/templates/src/components/storykeep/email-builder/Blocks.tsx +169 -0
- package/templates/src/components/storykeep/email-builder/EmailBuilder.tsx +223 -0
- package/templates/src/components/storykeep/email-builder/PreviewModal.tsx +136 -0
- package/templates/src/components/storykeep/email-builder/PropertyPanel.tsx +154 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard.tsx +1 -8
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Bookings.tsx +118 -14
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Emails.tsx +105 -0
- package/templates/src/constants.ts +2 -0
- package/templates/src/layouts/Layout.astro +8 -5
- package/templates/src/pages/api/google/oauth/callback.ts +50 -0
- package/templates/src/pages/api/google/oauth/disconnect.ts +32 -0
- package/templates/src/pages/api/google/oauth/start.ts +32 -0
- package/templates/src/pages/api/google/oauth/status.ts +32 -0
- package/templates/src/pages/privacy.astro +84 -0
- package/templates/src/pages/terms.astro +47 -0
- package/templates/src/stores/shopify.ts +21 -0
- package/templates/src/types/formTypes.ts +4 -2
- package/templates/src/types/tractstack.ts +35 -2
- package/templates/src/utils/api/advancedHelpers.ts +16 -0
- package/templates/src/utils/api/bookingHelpers.ts +3 -1
- package/templates/src/utils/api/brandConfig.ts +2 -0
- package/templates/src/utils/api/brandHelpers.ts +24 -1
- package/templates/src/utils/api/emailHelpers.ts +105 -0
- package/templates/src/utils/booking/appointmentMode.ts +135 -0
- package/templates/src/utils/customHelpers.ts +2 -0
- package/templates/src/utils/tenantResolver.ts +1 -1
- package/utils/inject-files.ts +63 -5
- package/templates/src/components/codehooks/SandboxAuthWrapper.tsx +0 -101
- package/templates/src/utils/actions/actionButton.ts +0 -103
- package/templates/src/utils/actions/preParse_Clicked.ts +0 -87
package/bin/create-tractstack.js
CHANGED
|
@@ -154,7 +154,7 @@ ${kleur.bold('Examples:')}
|
|
|
154
154
|
console.log(kleur.red("❌ This doesn't appear to be an Astro project."));
|
|
155
155
|
console.log('Please run this command in the root of your Astro project.\n');
|
|
156
156
|
console.log('To create a new Astro project with TractStack:');
|
|
157
|
-
console.log(kleur.cyan('pnpm create astro@
|
|
157
|
+
console.log(kleur.cyan('pnpm create astro@5 my-tractstack-site'));
|
|
158
158
|
console.log(kleur.cyan('cd my-tractstack-site'));
|
|
159
159
|
console.log(kleur.cyan('npx create-tractstack'));
|
|
160
160
|
process.exit(1);
|
|
@@ -332,7 +332,7 @@ PUBLIC_ENABLE_BUNNY="${finalResponses.enableBunny ? 'true' : 'false'}"
|
|
|
332
332
|
try {
|
|
333
333
|
// Install React and Node adapter
|
|
334
334
|
execSync(
|
|
335
|
-
`${addCommand} react@^19.0.0 react-dom@^19.0.0 @astrojs/react@^4.4.2 @astrojs/node@^9.4.3`,
|
|
335
|
+
`${addCommand} react@^19.0.0 react-dom@^19.0.0 astro@^5.16.6 @astrojs/react@^4.4.2 @astrojs/node@^9.4.3`,
|
|
336
336
|
{ stdio: 'inherit' }
|
|
337
337
|
);
|
|
338
338
|
console.log(kleur.green('✅ React and Node adapter installed'));
|
|
@@ -382,7 +382,7 @@ PUBLIC_ENABLE_BUNNY="${finalResponses.enableBunny ? 'true' : 'false'}"
|
|
|
382
382
|
console.log('Please run manually:');
|
|
383
383
|
console.log(
|
|
384
384
|
kleur.cyan(
|
|
385
|
-
`${addCommand} react@^19.0.0 react-dom@^19.0.0 @astrojs/react@^4.4.2 @astrojs/node@^9.4.3 @nanostores/react@^1.0.0 nanostores@^1.0.1 @nanostores/persistent ulid@^3.0.1 @ark-ui/react@^5.30.0 @heroicons/react@^2.1.1 @internationalized/date@3.10.0 d3@^7.9.0 d3-sankey@^0.12.3 recharts@^3.1.2 player.js@^0.1.0 tinycolor2@1.6.0 html-to-image@^1.11.13 path-to-regexp@^8.0.0 postcss postcss-selector-parser`
|
|
385
|
+
`${addCommand} react@^19.0.0 react-dom@^19.0.0 astro@^5.16.6 @astrojs/react@^4.4.2 @astrojs/node@^9.4.3 @nanostores/react@^1.0.0 nanostores@^1.0.1 @nanostores/persistent ulid@^3.0.1 @ark-ui/react@^5.30.0 @heroicons/react@^2.1.1 @internationalized/date@3.10.0 d3@^7.9.0 d3-sankey@^0.12.3 recharts@^3.1.2 player.js@^0.1.0 tinycolor2@1.6.0 html-to-image@^1.11.13 path-to-regexp@^8.0.0 postcss postcss-selector-parser`
|
|
386
386
|
)
|
|
387
387
|
);
|
|
388
388
|
console.log(
|
package/dist/index.js
CHANGED
|
@@ -2,13 +2,13 @@ import { fileURLToPath as d } from "node:url";
|
|
|
2
2
|
import { dirname as i, resolve as l } from "node:path";
|
|
3
3
|
import { existsSync as n, mkdirSync as x, copyFileSync as k, writeFileSync as u } from "node:fs";
|
|
4
4
|
import { resolve as a } from "path";
|
|
5
|
-
function
|
|
5
|
+
function g(t) {
|
|
6
6
|
const e = i(d(t));
|
|
7
7
|
return {
|
|
8
8
|
resolve: (...c) => l(e, ...c)
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
|
-
function
|
|
11
|
+
function b(t, e) {
|
|
12
12
|
e.info("TractStack configuration applied"), t.enableMultiTenant && e.info("Multi-tenant mode enabled"), t.includeExamples && e.info("Example components will be included");
|
|
13
13
|
const c = process.env.PUBLIC_GO_BACKEND, o = process.env.PUBLIC_TENANTID;
|
|
14
14
|
if (!c)
|
|
@@ -747,10 +747,6 @@ async function y(t, e, c) {
|
|
|
747
747
|
src: t("../templates/src/utils/actions/preParse_Action.ts"),
|
|
748
748
|
dest: "src/utils/actions/preParse_Action.ts"
|
|
749
749
|
},
|
|
750
|
-
{
|
|
751
|
-
src: t("../templates/src/utils/actions/preParse_Clicked.ts"),
|
|
752
|
-
dest: "src/utils/actions/preParse_Clicked.ts"
|
|
753
|
-
},
|
|
754
750
|
{
|
|
755
751
|
src: t("../templates/src/utils/actions/preParse_Impression.ts"),
|
|
756
752
|
dest: "src/utils/actions/preParse_Impression.ts"
|
|
@@ -891,6 +887,14 @@ async function y(t, e, c) {
|
|
|
891
887
|
src: t("../templates/custom/shopify/cart.astro"),
|
|
892
888
|
dest: "src/pages/cart.astro"
|
|
893
889
|
},
|
|
890
|
+
{
|
|
891
|
+
src: t("../templates/src/pages/privacy.astro"),
|
|
892
|
+
dest: "src/pages/privacy.astro"
|
|
893
|
+
},
|
|
894
|
+
{
|
|
895
|
+
src: t("../templates/src/pages/terms.astro"),
|
|
896
|
+
dest: "src/pages/terms.astro"
|
|
897
|
+
},
|
|
894
898
|
{
|
|
895
899
|
src: t("../templates/src/pages/404.astro"),
|
|
896
900
|
dest: "src/pages/404.astro"
|
|
@@ -989,6 +993,22 @@ async function y(t, e, c) {
|
|
|
989
993
|
src: t("../templates/src/pages/api/auth/logout.ts"),
|
|
990
994
|
dest: "src/pages/api/auth/logout.ts"
|
|
991
995
|
},
|
|
996
|
+
{
|
|
997
|
+
src: t("../templates/src/pages/api/google/oauth/start.ts"),
|
|
998
|
+
dest: "src/pages/api/google/oauth/start.ts"
|
|
999
|
+
},
|
|
1000
|
+
{
|
|
1001
|
+
src: t("../templates/src/pages/api/google/oauth/status.ts"),
|
|
1002
|
+
dest: "src/pages/api/google/oauth/status.ts"
|
|
1003
|
+
},
|
|
1004
|
+
{
|
|
1005
|
+
src: t("../templates/src/pages/api/google/oauth/disconnect.ts"),
|
|
1006
|
+
dest: "src/pages/api/google/oauth/disconnect.ts"
|
|
1007
|
+
},
|
|
1008
|
+
{
|
|
1009
|
+
src: t("../templates/src/pages/api/google/oauth/callback.ts"),
|
|
1010
|
+
dest: "src/pages/api/google/oauth/callback.ts"
|
|
1011
|
+
},
|
|
992
1012
|
{
|
|
993
1013
|
src: t("../templates/src/pages/api/orphan-analysis.ts"),
|
|
994
1014
|
dest: "src/pages/api/orphan-analysis.ts"
|
|
@@ -1242,6 +1262,36 @@ async function y(t, e, c) {
|
|
|
1242
1262
|
),
|
|
1243
1263
|
dest: "src/components/storykeep/shopify/ShopifyDashboard_Services.tsx"
|
|
1244
1264
|
},
|
|
1265
|
+
{
|
|
1266
|
+
src: t(
|
|
1267
|
+
"../templates/src/components/storykeep/shopify/ShopifyDashboard_Emails.tsx"
|
|
1268
|
+
),
|
|
1269
|
+
dest: "src/components/storykeep/shopify/ShopifyDashboard_Emails.tsx"
|
|
1270
|
+
},
|
|
1271
|
+
{
|
|
1272
|
+
src: t(
|
|
1273
|
+
"../templates/src/components/storykeep/email-builder/EmailBuilder.tsx"
|
|
1274
|
+
),
|
|
1275
|
+
dest: "src/components/storykeep/email-builder/EmailBuilder.tsx"
|
|
1276
|
+
},
|
|
1277
|
+
{
|
|
1278
|
+
src: t(
|
|
1279
|
+
"../templates/src/components/storykeep/email-builder/Blocks.tsx"
|
|
1280
|
+
),
|
|
1281
|
+
dest: "src/components/storykeep/email-builder/Blocks.tsx"
|
|
1282
|
+
},
|
|
1283
|
+
{
|
|
1284
|
+
src: t(
|
|
1285
|
+
"../templates/src/components/storykeep/email-builder/PropertyPanel.tsx"
|
|
1286
|
+
),
|
|
1287
|
+
dest: "src/components/storykeep/email-builder/PropertyPanel.tsx"
|
|
1288
|
+
},
|
|
1289
|
+
{
|
|
1290
|
+
src: t(
|
|
1291
|
+
"../templates/src/components/storykeep/email-builder/PreviewModal.tsx"
|
|
1292
|
+
),
|
|
1293
|
+
dest: "src/components/storykeep/email-builder/PreviewModal.tsx"
|
|
1294
|
+
},
|
|
1245
1295
|
{
|
|
1246
1296
|
src: t(
|
|
1247
1297
|
"../templates/src/components/storykeep/shopify/ShopifyDashboard_Search.tsx"
|
|
@@ -2230,6 +2280,10 @@ async function y(t, e, c) {
|
|
|
2230
2280
|
src: t("../templates/src/utils/api/setupHelpers.ts"),
|
|
2231
2281
|
dest: "src/utils/api/setupHelpers.ts"
|
|
2232
2282
|
},
|
|
2283
|
+
{
|
|
2284
|
+
src: t("../templates/src/utils/api/emailHelpers.ts"),
|
|
2285
|
+
dest: "src/utils/api/emailHelpers.ts"
|
|
2286
|
+
},
|
|
2233
2287
|
{
|
|
2234
2288
|
src: t(
|
|
2235
2289
|
"../templates/src/components/storykeep/widgets/HydrateWizard.tsx"
|
|
@@ -2273,6 +2327,11 @@ async function y(t, e, c) {
|
|
|
2273
2327
|
dest: "src/utils/customHelpers.ts",
|
|
2274
2328
|
protected: !0
|
|
2275
2329
|
},
|
|
2330
|
+
{
|
|
2331
|
+
src: t("../templates/src/utils/booking/appointmentMode.ts"),
|
|
2332
|
+
dest: "src/utils/booking/appointmentMode.ts",
|
|
2333
|
+
protected: !0
|
|
2334
|
+
},
|
|
2276
2335
|
{
|
|
2277
2336
|
src: t("../templates/custom/shopify/ShopifyProductGrid.tsx"),
|
|
2278
2337
|
dest: "src/custom/shopify/ShopifyProductGrid.tsx",
|
|
@@ -2372,7 +2431,7 @@ async function y(t, e, c) {
|
|
|
2372
2431
|
if (n(s.src))
|
|
2373
2432
|
k(s.src, s.dest), e.info(`Updated ${s.dest}`);
|
|
2374
2433
|
else {
|
|
2375
|
-
const m =
|
|
2434
|
+
const m = f(s.dest);
|
|
2376
2435
|
u(s.dest, m), e.info(`Created placeholder ${s.dest}`);
|
|
2377
2436
|
}
|
|
2378
2437
|
else s.protected ? e.info(`Protected: ${s.dest} (skipped overwrite)`) : e.info(`Skipped existing ${s.dest}`);
|
|
@@ -2381,24 +2440,23 @@ async function y(t, e, c) {
|
|
|
2381
2440
|
e.error(`Failed to create ${s.dest}: ${r}`);
|
|
2382
2441
|
}
|
|
2383
2442
|
}
|
|
2384
|
-
function
|
|
2443
|
+
function f(t) {
|
|
2385
2444
|
return t.endsWith(".astro") ? `---
|
|
2386
2445
|
// TractStack placeholder component
|
|
2387
2446
|
---
|
|
2388
2447
|
<div>TractStack placeholder: ${t}</div>` : t.endsWith(".tsx") ? `// TractStack placeholder component
|
|
2389
|
-
import React from 'react';
|
|
2390
2448
|
export default function Placeholder() {
|
|
2391
2449
|
return <div>TractStack placeholder: ${t}</div>;
|
|
2392
2450
|
}` : t.endsWith(".ts") ? `// TractStack placeholder utility
|
|
2393
2451
|
export const placeholder = "${t}";` : `# TractStack placeholder: ${t}`;
|
|
2394
2452
|
}
|
|
2395
2453
|
function P(t = {}) {
|
|
2396
|
-
const { resolve: e } =
|
|
2454
|
+
const { resolve: e } = g(import.meta.url);
|
|
2397
2455
|
return {
|
|
2398
2456
|
name: "astro-tractstack",
|
|
2399
2457
|
hooks: {
|
|
2400
2458
|
"astro:config:setup": async ({ config: c, updateConfig: o, logger: s }) => {
|
|
2401
|
-
|
|
2459
|
+
b(t, s);
|
|
2402
2460
|
const p = t.enableMultiTenant || !1;
|
|
2403
2461
|
if (s.info(
|
|
2404
2462
|
`DEBUG: enableMultiTenant = ${p}, process.env.PUBLIC_ENABLE_MULTI_TENANT = ${process.env.PUBLIC_ENABLE_MULTI_TENANT}`
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react';
|
|
1
|
+
import { useState, useEffect, useMemo } from 'react';
|
|
2
2
|
import { ulid } from 'ulid';
|
|
3
3
|
import { useStore } from '@nanostores/react';
|
|
4
4
|
import {
|
|
@@ -7,14 +7,18 @@ import {
|
|
|
7
7
|
cartState,
|
|
8
8
|
CART_STATES,
|
|
9
9
|
isShopifyHandoff,
|
|
10
|
+
preferredAppointmentMode,
|
|
10
11
|
transactionTraceId,
|
|
11
12
|
type CartItemState,
|
|
12
13
|
} from '@/stores/shopify';
|
|
13
14
|
import { getShopifyImage } from '@/utils/helpers';
|
|
15
|
+
import { deriveAppointmentConstraints } from '@/utils/booking/appointmentMode';
|
|
14
16
|
import type { ResourceNode } from '@/types/compositorTypes';
|
|
15
17
|
|
|
16
18
|
interface CartProps {
|
|
17
19
|
resources: ResourceNode[];
|
|
20
|
+
allowRemote?: boolean;
|
|
21
|
+
remoteOnly?: boolean;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
const getCleanVariantTitle = (variant: any) => {
|
|
@@ -34,9 +38,14 @@ const getCleanVariantTitle = (variant: any) => {
|
|
|
34
38
|
return title === 'Default Title' ? '' : title;
|
|
35
39
|
};
|
|
36
40
|
|
|
37
|
-
export default function Cart({
|
|
41
|
+
export default function Cart({
|
|
42
|
+
resources = [],
|
|
43
|
+
allowRemote = false,
|
|
44
|
+
remoteOnly = false,
|
|
45
|
+
}: CartProps) {
|
|
38
46
|
const cart = useStore(cartStore);
|
|
39
47
|
const isHandoff = useStore(isShopifyHandoff);
|
|
48
|
+
const appointmentMode = useStore(preferredAppointmentMode);
|
|
40
49
|
const [pickupEnabled, setPickupEnabled] = useState(false);
|
|
41
50
|
|
|
42
51
|
const cartValues = Object.values(cart);
|
|
@@ -67,12 +76,34 @@ export default function Cart({ resources = [] }: CartProps) {
|
|
|
67
76
|
return !!resource?.optionsPayload?.bookingLengthMinutes;
|
|
68
77
|
});
|
|
69
78
|
|
|
79
|
+
const appointmentConstraints = useMemo(
|
|
80
|
+
() =>
|
|
81
|
+
deriveAppointmentConstraints(cart, resources, {
|
|
82
|
+
allowRemote,
|
|
83
|
+
remoteOnly,
|
|
84
|
+
}),
|
|
85
|
+
[cart, resources, allowRemote, remoteOnly]
|
|
86
|
+
);
|
|
87
|
+
const { effectiveRemoteOnly, remoteAvailable, inPersonAvailable, canRemote } =
|
|
88
|
+
appointmentConstraints;
|
|
89
|
+
|
|
70
90
|
const hasPhysicalProductWithPickup = cartValues.some(
|
|
71
91
|
(item) =>
|
|
72
92
|
item.variantIdPickup && item.variantIdPickup !== item.variantIdShipped
|
|
73
93
|
);
|
|
74
94
|
|
|
75
|
-
const canPickup =
|
|
95
|
+
const canPickup =
|
|
96
|
+
hasService && hasPhysicalProductWithPickup && appointmentMode !== 'REMOTE';
|
|
97
|
+
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
if (effectiveRemoteOnly) {
|
|
100
|
+
preferredAppointmentMode.set('REMOTE');
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (!canRemote && preferredAppointmentMode.get() === 'REMOTE') {
|
|
104
|
+
preferredAppointmentMode.set('IN_PERSON');
|
|
105
|
+
}
|
|
106
|
+
}, [effectiveRemoteOnly, canRemote]);
|
|
76
107
|
|
|
77
108
|
useEffect(() => {
|
|
78
109
|
if (canPickup) {
|
|
@@ -131,17 +162,55 @@ export default function Cart({ resources = [] }: CartProps) {
|
|
|
131
162
|
<div className="rounded-lg bg-white shadow">
|
|
132
163
|
<div className="flex items-center justify-between border-b border-gray-200 px-6 py-4">
|
|
133
164
|
<h2 className="text-xl font-bold text-gray-800">Shopping Cart</h2>
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
<
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
className="
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
165
|
+
<div className="flex items-center gap-4">
|
|
166
|
+
{hasService && remoteAvailable && inPersonAvailable && (
|
|
167
|
+
<div className="flex flex-col items-end gap-1">
|
|
168
|
+
<p className="text-xs font-bold uppercase tracking-wide text-gray-500">
|
|
169
|
+
Appointment Mode
|
|
170
|
+
</p>
|
|
171
|
+
<div className="flex gap-2">
|
|
172
|
+
<button
|
|
173
|
+
type="button"
|
|
174
|
+
onClick={() => preferredAppointmentMode.set('IN_PERSON')}
|
|
175
|
+
className={`rounded-md px-3 py-2 text-sm font-bold ${
|
|
176
|
+
appointmentMode === 'IN_PERSON'
|
|
177
|
+
? 'bg-black text-white'
|
|
178
|
+
: 'border border-gray-300 bg-white text-gray-700'
|
|
179
|
+
}`}
|
|
180
|
+
>
|
|
181
|
+
In Person
|
|
182
|
+
</button>
|
|
183
|
+
<button
|
|
184
|
+
type="button"
|
|
185
|
+
onClick={() => preferredAppointmentMode.set('REMOTE')}
|
|
186
|
+
className={`rounded-md px-3 py-2 text-sm font-bold ${
|
|
187
|
+
appointmentMode === 'REMOTE'
|
|
188
|
+
? 'bg-black text-white'
|
|
189
|
+
: 'border border-gray-300 bg-white text-gray-700'
|
|
190
|
+
}`}
|
|
191
|
+
>
|
|
192
|
+
Remote
|
|
193
|
+
</button>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
)}
|
|
197
|
+
{hasService && effectiveRemoteOnly && (
|
|
198
|
+
<p className="text-sm font-bold text-gray-900">
|
|
199
|
+
Appointment: Remote
|
|
200
|
+
</p>
|
|
201
|
+
)}
|
|
202
|
+
{canPickup && (
|
|
203
|
+
<label className="flex items-center space-x-2 text-sm font-bold text-gray-900">
|
|
204
|
+
<input
|
|
205
|
+
type="checkbox"
|
|
206
|
+
checked={pickupEnabled}
|
|
207
|
+
onChange={(e) => setPickupEnabled(e.target.checked)}
|
|
208
|
+
className="h-4 w-4 rounded border-gray-300 text-black focus:ring-black"
|
|
209
|
+
/>
|
|
210
|
+
<span>Pick up at Store</span>
|
|
211
|
+
</label>
|
|
212
|
+
)}
|
|
213
|
+
</div>
|
|
145
214
|
</div>
|
|
146
215
|
|
|
147
216
|
<ul className="divide-y divide-gray-200">
|
|
@@ -163,9 +232,9 @@ export default function Cart({ resources = [] }: CartProps) {
|
|
|
163
232
|
? firstItem.variantIdPickup
|
|
164
233
|
: firstItem.variantIdShipped;
|
|
165
234
|
const displayIdFirst =
|
|
235
|
+
firstItem.variantId ||
|
|
166
236
|
activeVariantIdFirst ||
|
|
167
|
-
firstItem.variantIdPickup
|
|
168
|
-
firstItem.variantId;
|
|
237
|
+
firstItem.variantIdPickup;
|
|
169
238
|
|
|
170
239
|
const { src, srcSet } = getShopifyImage(
|
|
171
240
|
resource,
|
|
@@ -240,9 +309,9 @@ export default function Cart({ resources = [] }: CartProps) {
|
|
|
240
309
|
: item.variantIdShipped;
|
|
241
310
|
|
|
242
311
|
const displayId =
|
|
312
|
+
item.variantId ||
|
|
243
313
|
activeVariantId ||
|
|
244
|
-
item.variantIdPickup
|
|
245
|
-
item.variantId;
|
|
314
|
+
item.variantIdPickup;
|
|
246
315
|
|
|
247
316
|
let price = '0.00';
|
|
248
317
|
let currency = 'USD';
|
|
@@ -348,13 +417,24 @@ export default function Cart({ resources = [] }: CartProps) {
|
|
|
348
417
|
const sanitizedCart = { ...currentCart };
|
|
349
418
|
|
|
350
419
|
Object.keys(sanitizedCart).forEach((key) => {
|
|
351
|
-
const item = sanitizedCart[key];
|
|
420
|
+
const item = { ...sanitizedCart[key] };
|
|
421
|
+
|
|
422
|
+
if (
|
|
423
|
+
item.variantId &&
|
|
424
|
+
!item.variantIdShipped &&
|
|
425
|
+
!item.variantIdPickup
|
|
426
|
+
) {
|
|
427
|
+
sanitizedCart[key] = item;
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
352
430
|
|
|
353
431
|
if (isPickupMode && item.variantIdPickup) {
|
|
354
432
|
item.variantId = item.variantIdPickup;
|
|
355
433
|
} else if (!isPickupMode && item.variantIdShipped) {
|
|
356
434
|
item.variantId = item.variantIdShipped;
|
|
357
435
|
}
|
|
436
|
+
|
|
437
|
+
sanitizedCart[key] = item;
|
|
358
438
|
});
|
|
359
439
|
|
|
360
440
|
cartStore.set(sanitizedCart);
|