@foxpixel/react 0.1.1 → 0.2.1
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/dist/index.d.mts +309 -3
- package/dist/index.d.ts +309 -3
- package/dist/index.js +767 -97
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +755 -97
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -3
package/dist/index.js
CHANGED
|
@@ -31,11 +31,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
AuthProvider: () => AuthProvider,
|
|
34
|
+
Editable: () => Editable,
|
|
35
|
+
EditableHTML: () => EditableHTML,
|
|
36
|
+
EditableImage: () => EditableImage,
|
|
34
37
|
FoxPixelHttpClient: () => FoxPixelHttpClient,
|
|
35
38
|
FoxPixelProvider: () => FoxPixelProvider,
|
|
36
39
|
GuestOnlyRoute: () => GuestOnlyRoute,
|
|
37
40
|
ProtectedRoute: () => ProtectedRoute,
|
|
41
|
+
SITE_CONTENT_QUERY_KEY: () => SITE_CONTENT_QUERY_KEY,
|
|
38
42
|
getBlogPostSchemaLd: () => getBlogPostSchemaLd,
|
|
43
|
+
prefetchSiteContent: () => prefetchSiteContent,
|
|
39
44
|
useAdminBlogAnalytics: () => useAdminBlogAnalytics,
|
|
40
45
|
useAdminBlogCategories: () => useAdminBlogCategories,
|
|
41
46
|
useAdminBlogComments: () => useAdminBlogComments,
|
|
@@ -55,17 +60,24 @@ __export(index_exports, {
|
|
|
55
60
|
useBlogPosts: () => useBlogPosts,
|
|
56
61
|
useBlogTags: () => useBlogTags,
|
|
57
62
|
useContactCapture: () => useContactCapture,
|
|
63
|
+
useEditMode: () => useEditMode,
|
|
64
|
+
useEditModeMessaging: () => useEditModeMessaging,
|
|
58
65
|
useFoxPixelContext: () => useFoxPixelContext,
|
|
59
66
|
useLeadCapture: () => useLeadCapture,
|
|
60
67
|
useNewsletterSubscribe: () => useNewsletterSubscribe,
|
|
61
68
|
useNewsletterUnsubscribe: () => useNewsletterUnsubscribe,
|
|
69
|
+
useSendEditRequest: () => useSendEditRequest,
|
|
62
70
|
useServices: () => useServices,
|
|
71
|
+
useSiteContent: () => useSiteContent,
|
|
72
|
+
useSiteContentQuery: () => useSiteContentQuery,
|
|
73
|
+
useSiteContentSection: () => useSiteContentSection,
|
|
74
|
+
useSiteContents: () => useSiteContents,
|
|
63
75
|
withAuth: () => withAuth
|
|
64
76
|
});
|
|
65
77
|
module.exports = __toCommonJS(index_exports);
|
|
66
78
|
|
|
67
79
|
// src/context/FoxPixelContext.tsx
|
|
68
|
-
var import_react = require("react");
|
|
80
|
+
var import_react = __toESM(require("react"));
|
|
69
81
|
|
|
70
82
|
// src/client/http.ts
|
|
71
83
|
var import_axios = __toESM(require("axios"));
|
|
@@ -177,15 +189,21 @@ var FoxPixelHttpClient = class {
|
|
|
177
189
|
|
|
178
190
|
// src/context/FoxPixelContext.tsx
|
|
179
191
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
192
|
+
if (!import_react.default || typeof import_react.default.useMemo !== "function") {
|
|
193
|
+
throw new Error(
|
|
194
|
+
'@foxpixel/react: React is not available. Ensure your app uses a single React instance and the SDK is not bundled with a different React. In Next.js use transpilePackages: ["@foxpixel/react"] and ensure react/react-dom resolve to one module (see next.config.js).'
|
|
195
|
+
);
|
|
196
|
+
}
|
|
180
197
|
var FoxPixelContext = (0, import_react.createContext)(null);
|
|
181
|
-
function FoxPixelProvider({ children, config = {} }) {
|
|
198
|
+
function FoxPixelProvider({ children, config = {}, queryClient }) {
|
|
182
199
|
const client = (0, import_react.useMemo)(() => {
|
|
183
200
|
return new FoxPixelHttpClient(config);
|
|
184
201
|
}, [config.apiUrl, config.apiKey, config.tenantId]);
|
|
185
202
|
const value = (0, import_react.useMemo)(() => ({
|
|
186
203
|
client,
|
|
187
|
-
config
|
|
188
|
-
|
|
204
|
+
config,
|
|
205
|
+
queryClient: queryClient ?? null
|
|
206
|
+
}), [client, config, queryClient]);
|
|
189
207
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FoxPixelContext.Provider, { value, children });
|
|
190
208
|
}
|
|
191
209
|
function useFoxPixelContext() {
|
|
@@ -304,7 +322,8 @@ function AuthProvider({
|
|
|
304
322
|
logout,
|
|
305
323
|
register,
|
|
306
324
|
updateProfile,
|
|
307
|
-
refetch: fetchCurrentUser
|
|
325
|
+
refetch: fetchCurrentUser,
|
|
326
|
+
hasPermission: (permission) => user !== null && permission === "site:content:update"
|
|
308
327
|
};
|
|
309
328
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AuthContext.Provider, { value, children });
|
|
310
329
|
}
|
|
@@ -410,13 +429,473 @@ function withAuth(Component, options = {}) {
|
|
|
410
429
|
};
|
|
411
430
|
}
|
|
412
431
|
|
|
413
|
-
// src/
|
|
432
|
+
// src/components/Editable.tsx
|
|
433
|
+
var import_react8 = require("react");
|
|
434
|
+
|
|
435
|
+
// src/hooks/useEditMode.ts
|
|
414
436
|
var import_react6 = require("react");
|
|
437
|
+
var SITE_CONTENT_QUERY_KEY = "siteContent";
|
|
438
|
+
function useEditMode() {
|
|
439
|
+
const [isEditMode, setIsEditMode] = (0, import_react6.useState)(false);
|
|
440
|
+
(0, import_react6.useEffect)(() => {
|
|
441
|
+
if (typeof window === "undefined") return;
|
|
442
|
+
const params = new URLSearchParams(window.location.search);
|
|
443
|
+
setIsEditMode(params.get("edit-mode") === "true");
|
|
444
|
+
}, []);
|
|
445
|
+
return isEditMode;
|
|
446
|
+
}
|
|
447
|
+
function useEditModeMessaging() {
|
|
448
|
+
const ctx = useFoxPixelContext();
|
|
449
|
+
const queryClient = ctx?.queryClient ?? null;
|
|
450
|
+
const isEditMode = useEditMode();
|
|
451
|
+
(0, import_react6.useEffect)(() => {
|
|
452
|
+
if (!isEditMode || typeof window === "undefined") return;
|
|
453
|
+
window.parent.postMessage({ type: "FOXPIXEL_READY" }, "*");
|
|
454
|
+
const handleMessage = (event) => {
|
|
455
|
+
if (!queryClient) return;
|
|
456
|
+
const { type, payload } = event.data || {};
|
|
457
|
+
if (type !== "FOXPIXEL_CONTENT_UPDATED" || !payload?.contentKey) return;
|
|
458
|
+
const { contentKey, newValue } = payload;
|
|
459
|
+
if (typeof newValue === "string") {
|
|
460
|
+
queryClient.setQueryData(
|
|
461
|
+
[SITE_CONTENT_QUERY_KEY, contentKey],
|
|
462
|
+
(prev) => ({
|
|
463
|
+
value: newValue,
|
|
464
|
+
contentType: prev?.contentType ?? "TEXT"
|
|
465
|
+
})
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
queryClient.invalidateQueries({
|
|
469
|
+
queryKey: [SITE_CONTENT_QUERY_KEY, contentKey]
|
|
470
|
+
});
|
|
471
|
+
};
|
|
472
|
+
window.addEventListener("message", handleMessage);
|
|
473
|
+
return () => window.removeEventListener("message", handleMessage);
|
|
474
|
+
}, [isEditMode, queryClient]);
|
|
475
|
+
return isEditMode;
|
|
476
|
+
}
|
|
477
|
+
function useSendEditRequest() {
|
|
478
|
+
const isEditMode = useEditMode();
|
|
479
|
+
return (0, import_react6.useCallback)(
|
|
480
|
+
(contentKey, currentValue, contentType = "text", section, description) => {
|
|
481
|
+
if (!isEditMode) return;
|
|
482
|
+
if (typeof window !== "undefined" && window.parent !== window) {
|
|
483
|
+
window.parent.postMessage(
|
|
484
|
+
{
|
|
485
|
+
type: "FOXPIXEL_EDIT_CONTENT",
|
|
486
|
+
payload: {
|
|
487
|
+
contentKey,
|
|
488
|
+
currentValue,
|
|
489
|
+
contentType,
|
|
490
|
+
section,
|
|
491
|
+
description
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
"*"
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
[isEditMode]
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// src/hooks/useSiteContentQuery.ts
|
|
503
|
+
var import_react7 = require("react");
|
|
504
|
+
function getCached(queryClient, contentKey) {
|
|
505
|
+
const data = queryClient.getQueryData([
|
|
506
|
+
SITE_CONTENT_QUERY_KEY,
|
|
507
|
+
contentKey
|
|
508
|
+
]);
|
|
509
|
+
if (data == null) return void 0;
|
|
510
|
+
return { value: data.value ?? "", contentType: data.contentType ?? "TEXT" };
|
|
511
|
+
}
|
|
512
|
+
function useSiteContentQuery(contentKey, options) {
|
|
513
|
+
const { defaultValue } = options;
|
|
514
|
+
const { client, queryClient } = useFoxPixelContext();
|
|
515
|
+
const [state, setState] = (0, import_react7.useState)(() => {
|
|
516
|
+
if (queryClient) {
|
|
517
|
+
const cached = getCached(queryClient, contentKey);
|
|
518
|
+
if (cached) {
|
|
519
|
+
return { value: cached.value, isLoading: false, contentType: cached.contentType };
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return { value: defaultValue, isLoading: true, contentType: "TEXT" };
|
|
523
|
+
});
|
|
524
|
+
const contentKeyRef = (0, import_react7.useRef)(contentKey);
|
|
525
|
+
contentKeyRef.current = contentKey;
|
|
526
|
+
(0, import_react7.useEffect)(() => {
|
|
527
|
+
if (!queryClient) {
|
|
528
|
+
setState((s) => ({ ...s, value: defaultValue, isLoading: false }));
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
const key = contentKeyRef.current;
|
|
532
|
+
const queryKey = [SITE_CONTENT_QUERY_KEY, key];
|
|
533
|
+
const queryFn = async () => {
|
|
534
|
+
try {
|
|
535
|
+
const content = await client.get(
|
|
536
|
+
`/api/site/content/${encodeURIComponent(key)}`
|
|
537
|
+
);
|
|
538
|
+
if (!content) return null;
|
|
539
|
+
return {
|
|
540
|
+
value: content.value ?? "",
|
|
541
|
+
contentType: content.contentType ?? "TEXT"
|
|
542
|
+
};
|
|
543
|
+
} catch (err) {
|
|
544
|
+
const status = err?.response?.status;
|
|
545
|
+
if (status === 404) return null;
|
|
546
|
+
throw err;
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
let cancelled = false;
|
|
550
|
+
queryClient.fetchQuery({
|
|
551
|
+
queryKey,
|
|
552
|
+
queryFn,
|
|
553
|
+
staleTime: 1e3 * 60 * 5,
|
|
554
|
+
retry: 1
|
|
555
|
+
}).then((data) => {
|
|
556
|
+
if (cancelled) return;
|
|
557
|
+
setState({
|
|
558
|
+
value: data?.value ?? defaultValue,
|
|
559
|
+
isLoading: false,
|
|
560
|
+
contentType: data?.contentType ?? "TEXT"
|
|
561
|
+
});
|
|
562
|
+
}).catch(() => {
|
|
563
|
+
if (cancelled) return;
|
|
564
|
+
setState((s) => ({ ...s, value: defaultValue, isLoading: false }));
|
|
565
|
+
});
|
|
566
|
+
const unsub = queryClient.getQueryCache().subscribe((event) => {
|
|
567
|
+
if (event?.type === "updated" && event?.query?.queryKey[1] === key) {
|
|
568
|
+
const cached = getCached(queryClient, key);
|
|
569
|
+
if (cached && !cancelled) {
|
|
570
|
+
setState({
|
|
571
|
+
value: cached.value,
|
|
572
|
+
isLoading: false,
|
|
573
|
+
contentType: cached.contentType
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
return () => {
|
|
579
|
+
cancelled = true;
|
|
580
|
+
unsub();
|
|
581
|
+
};
|
|
582
|
+
}, [queryClient, contentKey, defaultValue, client]);
|
|
583
|
+
return state;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// src/utils/sanitize.ts
|
|
587
|
+
var import_sanitize_html = __toESM(require("sanitize-html"));
|
|
588
|
+
var DEFAULT_ALLOWED_TAGS = [
|
|
589
|
+
"p",
|
|
590
|
+
"br",
|
|
591
|
+
"strong",
|
|
592
|
+
"em",
|
|
593
|
+
"u",
|
|
594
|
+
"s",
|
|
595
|
+
"a",
|
|
596
|
+
"ul",
|
|
597
|
+
"ol",
|
|
598
|
+
"li",
|
|
599
|
+
"h1",
|
|
600
|
+
"h2",
|
|
601
|
+
"h3",
|
|
602
|
+
"h4",
|
|
603
|
+
"h5",
|
|
604
|
+
"h6",
|
|
605
|
+
"blockquote",
|
|
606
|
+
"code",
|
|
607
|
+
"pre",
|
|
608
|
+
"span",
|
|
609
|
+
"div",
|
|
610
|
+
"img",
|
|
611
|
+
"table",
|
|
612
|
+
"thead",
|
|
613
|
+
"tbody",
|
|
614
|
+
"tr",
|
|
615
|
+
"th",
|
|
616
|
+
"td"
|
|
617
|
+
];
|
|
618
|
+
var DEFAULT_ALLOWED_ATTR = {
|
|
619
|
+
a: ["href", "target", "rel", "title"],
|
|
620
|
+
img: ["src", "alt", "title", "width", "height"],
|
|
621
|
+
"*": ["class"]
|
|
622
|
+
};
|
|
623
|
+
function sanitizeHtml(html) {
|
|
624
|
+
if (typeof html !== "string") return "";
|
|
625
|
+
return (0, import_sanitize_html.default)(html, {
|
|
626
|
+
allowedTags: DEFAULT_ALLOWED_TAGS,
|
|
627
|
+
allowedAttributes: DEFAULT_ALLOWED_ATTR
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// src/utils/cn.ts
|
|
632
|
+
function cn(...args) {
|
|
633
|
+
return args.filter(Boolean).join(" ");
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// src/components/Editable.tsx
|
|
637
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
638
|
+
var EDIT_MODE_TOOLTIP_STYLE = {
|
|
639
|
+
position: "absolute",
|
|
640
|
+
top: "-28px",
|
|
641
|
+
left: "50%",
|
|
642
|
+
transform: "translateX(-50%)",
|
|
643
|
+
fontSize: "10px",
|
|
644
|
+
fontFamily: "system-ui, sans-serif",
|
|
645
|
+
padding: "4px 8px",
|
|
646
|
+
backgroundColor: "rgb(37 99 235)",
|
|
647
|
+
color: "white",
|
|
648
|
+
borderRadius: "4px",
|
|
649
|
+
whiteSpace: "nowrap",
|
|
650
|
+
pointerEvents: "none",
|
|
651
|
+
zIndex: 100,
|
|
652
|
+
boxShadow: "0 1px 3px rgba(0,0,0,0.2)",
|
|
653
|
+
transition: "opacity 0.15s ease"
|
|
654
|
+
};
|
|
655
|
+
function Editable({
|
|
656
|
+
contentKey,
|
|
657
|
+
defaultValue,
|
|
658
|
+
as = "span",
|
|
659
|
+
multiline = false,
|
|
660
|
+
className
|
|
661
|
+
}) {
|
|
662
|
+
const [isHovered, setIsHovered] = (0, import_react8.useState)(false);
|
|
663
|
+
const isEditMode = useEditModeMessaging();
|
|
664
|
+
const sendEditRequest = useSendEditRequest();
|
|
665
|
+
const { value, isLoading, contentType } = useSiteContentQuery(contentKey, {
|
|
666
|
+
defaultValue
|
|
667
|
+
});
|
|
668
|
+
const section = contentKey.includes(".") ? contentKey.split(".")[0] : void 0;
|
|
669
|
+
const handleClick = (0, import_react8.useCallback)(
|
|
670
|
+
(e) => {
|
|
671
|
+
if (isEditMode) {
|
|
672
|
+
e.preventDefault();
|
|
673
|
+
e.stopPropagation();
|
|
674
|
+
sendEditRequest(
|
|
675
|
+
contentKey,
|
|
676
|
+
value,
|
|
677
|
+
contentType?.toLowerCase() || "text",
|
|
678
|
+
section
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
},
|
|
682
|
+
[isEditMode, contentKey, value, contentType, section, sendEditRequest]
|
|
683
|
+
);
|
|
684
|
+
if (isLoading) {
|
|
685
|
+
return (0, import_react8.createElement)(as, {
|
|
686
|
+
className: cn(
|
|
687
|
+
"animate-pulse bg-muted rounded",
|
|
688
|
+
multiline ? "h-20" : "h-6",
|
|
689
|
+
"inline-block min-w-[100px]",
|
|
690
|
+
className
|
|
691
|
+
),
|
|
692
|
+
"aria-busy": true,
|
|
693
|
+
"aria-label": "Loading content..."
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
const editModeWrapperStyle = isEditMode ? {
|
|
697
|
+
position: "relative",
|
|
698
|
+
display: "inline",
|
|
699
|
+
cursor: "pointer",
|
|
700
|
+
borderRadius: "2px",
|
|
701
|
+
outline: isHovered ? "2px solid rgb(59 130 246)" : "none",
|
|
702
|
+
outlineOffset: "2px",
|
|
703
|
+
backgroundColor: isHovered ? "rgba(59 130 246 / 0.08)" : void 0
|
|
704
|
+
} : {};
|
|
705
|
+
const tooltipSpan = isEditMode ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
706
|
+
"span",
|
|
707
|
+
{
|
|
708
|
+
style: {
|
|
709
|
+
...EDIT_MODE_TOOLTIP_STYLE,
|
|
710
|
+
opacity: isHovered ? 1 : 0
|
|
711
|
+
},
|
|
712
|
+
"aria-hidden": true,
|
|
713
|
+
children: "Click to edit"
|
|
714
|
+
}
|
|
715
|
+
) : null;
|
|
716
|
+
const hoverHandlers = isEditMode ? {
|
|
717
|
+
onMouseEnter: () => setIsHovered(true),
|
|
718
|
+
onMouseLeave: () => setIsHovered(false)
|
|
719
|
+
} : {};
|
|
720
|
+
if (multiline && value.includes("\n")) {
|
|
721
|
+
const safeBr = sanitizeHtml(value.replace(/\n/g, "<br />"));
|
|
722
|
+
return (0, import_react8.createElement)(
|
|
723
|
+
"span",
|
|
724
|
+
{ style: editModeWrapperStyle, ...hoverHandlers },
|
|
725
|
+
(0, import_react8.createElement)(as, {
|
|
726
|
+
className,
|
|
727
|
+
"data-content-key": contentKey,
|
|
728
|
+
"data-editable": isEditMode ? "true" : void 0,
|
|
729
|
+
onClick: isEditMode ? handleClick : void 0,
|
|
730
|
+
dangerouslySetInnerHTML: { __html: safeBr }
|
|
731
|
+
}),
|
|
732
|
+
tooltipSpan
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
return (0, import_react8.createElement)(
|
|
736
|
+
"span",
|
|
737
|
+
{ style: editModeWrapperStyle, ...hoverHandlers },
|
|
738
|
+
(0, import_react8.createElement)(
|
|
739
|
+
as,
|
|
740
|
+
{
|
|
741
|
+
className,
|
|
742
|
+
"data-content-key": contentKey,
|
|
743
|
+
"data-editable": isEditMode ? "true" : void 0,
|
|
744
|
+
onClick: isEditMode ? handleClick : void 0
|
|
745
|
+
},
|
|
746
|
+
value
|
|
747
|
+
),
|
|
748
|
+
tooltipSpan
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
function EditableHTML({
|
|
752
|
+
contentKey,
|
|
753
|
+
defaultValue,
|
|
754
|
+
as = "div",
|
|
755
|
+
className
|
|
756
|
+
}) {
|
|
757
|
+
const [isHovered, setIsHovered] = (0, import_react8.useState)(false);
|
|
758
|
+
const isEditMode = useEditModeMessaging();
|
|
759
|
+
const sendEditRequest = useSendEditRequest();
|
|
760
|
+
const { value, isLoading } = useSiteContentQuery(contentKey, {
|
|
761
|
+
defaultValue
|
|
762
|
+
});
|
|
763
|
+
const section = contentKey.includes(".") ? contentKey.split(".")[0] : void 0;
|
|
764
|
+
const handleClick = (0, import_react8.useCallback)(
|
|
765
|
+
(e) => {
|
|
766
|
+
if (isEditMode) {
|
|
767
|
+
e.preventDefault();
|
|
768
|
+
e.stopPropagation();
|
|
769
|
+
sendEditRequest(contentKey, value, "html", section);
|
|
770
|
+
}
|
|
771
|
+
},
|
|
772
|
+
[isEditMode, contentKey, value, section, sendEditRequest]
|
|
773
|
+
);
|
|
774
|
+
if (isLoading) {
|
|
775
|
+
return (0, import_react8.createElement)(as, {
|
|
776
|
+
className: cn("animate-pulse bg-muted rounded h-32", className),
|
|
777
|
+
"aria-busy": true
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
const wrapperStyle = isEditMode ? {
|
|
781
|
+
position: "relative",
|
|
782
|
+
cursor: "pointer",
|
|
783
|
+
borderRadius: "2px",
|
|
784
|
+
outline: isHovered ? "2px solid rgb(59 130 246)" : "none",
|
|
785
|
+
outlineOffset: "2px",
|
|
786
|
+
backgroundColor: isHovered ? "rgba(59 130 246 / 0.08)" : void 0
|
|
787
|
+
} : {};
|
|
788
|
+
const safeHtml = sanitizeHtml(value);
|
|
789
|
+
const hoverHandlers = isEditMode ? { onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false) } : {};
|
|
790
|
+
return (0, import_react8.createElement)(
|
|
791
|
+
"div",
|
|
792
|
+
{ style: wrapperStyle, ...hoverHandlers },
|
|
793
|
+
(0, import_react8.createElement)(as, {
|
|
794
|
+
className: cn("prose prose-slate dark:prose-invert", className),
|
|
795
|
+
"data-content-key": contentKey,
|
|
796
|
+
"data-editable": isEditMode ? "true" : void 0,
|
|
797
|
+
onClick: isEditMode ? handleClick : void 0,
|
|
798
|
+
dangerouslySetInnerHTML: { __html: safeHtml }
|
|
799
|
+
}),
|
|
800
|
+
isEditMode && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
801
|
+
"span",
|
|
802
|
+
{
|
|
803
|
+
style: {
|
|
804
|
+
...EDIT_MODE_TOOLTIP_STYLE,
|
|
805
|
+
top: "-24px",
|
|
806
|
+
opacity: isHovered ? 1 : 0
|
|
807
|
+
},
|
|
808
|
+
"aria-hidden": true,
|
|
809
|
+
children: "Click to edit"
|
|
810
|
+
}
|
|
811
|
+
)
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
function EditableImage({
|
|
815
|
+
contentKey,
|
|
816
|
+
defaultValue,
|
|
817
|
+
alt,
|
|
818
|
+
className,
|
|
819
|
+
width,
|
|
820
|
+
height,
|
|
821
|
+
priority = false
|
|
822
|
+
}) {
|
|
823
|
+
const [isHovered, setIsHovered] = (0, import_react8.useState)(false);
|
|
824
|
+
const isEditMode = useEditModeMessaging();
|
|
825
|
+
const sendEditRequest = useSendEditRequest();
|
|
826
|
+
const { value: src, isLoading } = useSiteContentQuery(contentKey, {
|
|
827
|
+
defaultValue
|
|
828
|
+
});
|
|
829
|
+
const section = contentKey.includes(".") ? contentKey.split(".")[0] : void 0;
|
|
830
|
+
const handleClick = (0, import_react8.useCallback)(
|
|
831
|
+
(e) => {
|
|
832
|
+
if (isEditMode) {
|
|
833
|
+
e.preventDefault();
|
|
834
|
+
e.stopPropagation();
|
|
835
|
+
sendEditRequest(contentKey, src, "image", section);
|
|
836
|
+
}
|
|
837
|
+
},
|
|
838
|
+
[isEditMode, contentKey, src, section, sendEditRequest]
|
|
839
|
+
);
|
|
840
|
+
if (isLoading) {
|
|
841
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
842
|
+
"div",
|
|
843
|
+
{
|
|
844
|
+
className: cn("animate-pulse bg-muted rounded", className),
|
|
845
|
+
style: { width, height },
|
|
846
|
+
"aria-busy": "true"
|
|
847
|
+
}
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
const wrapperStyle = isEditMode ? {
|
|
851
|
+
position: "relative",
|
|
852
|
+
display: "inline-block",
|
|
853
|
+
cursor: "pointer",
|
|
854
|
+
borderRadius: "2px",
|
|
855
|
+
outline: isHovered ? "2px solid rgb(59 130 246)" : "none",
|
|
856
|
+
outlineOffset: "2px"
|
|
857
|
+
} : {};
|
|
858
|
+
const hoverHandlers = isEditMode ? { onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false) } : {};
|
|
859
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: wrapperStyle, ...hoverHandlers, children: [
|
|
860
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
861
|
+
"img",
|
|
862
|
+
{
|
|
863
|
+
src,
|
|
864
|
+
alt,
|
|
865
|
+
className,
|
|
866
|
+
style: isEditMode && isHovered ? { opacity: 0.95 } : void 0,
|
|
867
|
+
width,
|
|
868
|
+
height,
|
|
869
|
+
loading: priority ? "eager" : "lazy",
|
|
870
|
+
"data-content-key": contentKey,
|
|
871
|
+
"data-editable": isEditMode ? "true" : void 0,
|
|
872
|
+
onClick: isEditMode ? handleClick : void 0
|
|
873
|
+
}
|
|
874
|
+
),
|
|
875
|
+
isEditMode && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
876
|
+
"span",
|
|
877
|
+
{
|
|
878
|
+
style: {
|
|
879
|
+
...EDIT_MODE_TOOLTIP_STYLE,
|
|
880
|
+
top: "8px",
|
|
881
|
+
left: "8px",
|
|
882
|
+
transform: "none",
|
|
883
|
+
opacity: isHovered ? 1 : 0
|
|
884
|
+
},
|
|
885
|
+
"aria-hidden": true,
|
|
886
|
+
children: "Click to edit image"
|
|
887
|
+
}
|
|
888
|
+
)
|
|
889
|
+
] });
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// src/hooks/useServices.ts
|
|
893
|
+
var import_react9 = require("react");
|
|
415
894
|
function useServices(options = {}) {
|
|
416
895
|
const { client } = useFoxPixelContext();
|
|
417
|
-
const [services, setServices] = (0,
|
|
418
|
-
const [isLoading, setIsLoading] = (0,
|
|
419
|
-
const [error, setError] = (0,
|
|
896
|
+
const [services, setServices] = (0, import_react9.useState)(null);
|
|
897
|
+
const [isLoading, setIsLoading] = (0, import_react9.useState)(true);
|
|
898
|
+
const [error, setError] = (0, import_react9.useState)(null);
|
|
420
899
|
const fetchServices = async () => {
|
|
421
900
|
try {
|
|
422
901
|
setIsLoading(true);
|
|
@@ -434,7 +913,7 @@ function useServices(options = {}) {
|
|
|
434
913
|
setIsLoading(false);
|
|
435
914
|
}
|
|
436
915
|
};
|
|
437
|
-
(0,
|
|
916
|
+
(0, import_react9.useEffect)(() => {
|
|
438
917
|
fetchServices();
|
|
439
918
|
}, [options.category, options.active]);
|
|
440
919
|
return {
|
|
@@ -446,11 +925,11 @@ function useServices(options = {}) {
|
|
|
446
925
|
}
|
|
447
926
|
|
|
448
927
|
// src/hooks/useLeadCapture.ts
|
|
449
|
-
var
|
|
928
|
+
var import_react10 = require("react");
|
|
450
929
|
function useLeadCapture() {
|
|
451
930
|
const { client } = useFoxPixelContext();
|
|
452
|
-
const [isLoading, setIsLoading] = (0,
|
|
453
|
-
const [error, setError] = (0,
|
|
931
|
+
const [isLoading, setIsLoading] = (0, import_react10.useState)(false);
|
|
932
|
+
const [error, setError] = (0, import_react10.useState)(null);
|
|
454
933
|
const captureLead = async (data) => {
|
|
455
934
|
try {
|
|
456
935
|
setIsLoading(true);
|
|
@@ -473,11 +952,11 @@ function useLeadCapture() {
|
|
|
473
952
|
}
|
|
474
953
|
|
|
475
954
|
// src/hooks/useContactCapture.ts
|
|
476
|
-
var
|
|
955
|
+
var import_react11 = require("react");
|
|
477
956
|
function useContactCapture() {
|
|
478
957
|
const { client } = useFoxPixelContext();
|
|
479
|
-
const [isLoading, setIsLoading] = (0,
|
|
480
|
-
const [error, setError] = (0,
|
|
958
|
+
const [isLoading, setIsLoading] = (0, import_react11.useState)(false);
|
|
959
|
+
const [error, setError] = (0, import_react11.useState)(null);
|
|
481
960
|
const captureContact = async (data) => {
|
|
482
961
|
try {
|
|
483
962
|
setIsLoading(true);
|
|
@@ -499,13 +978,174 @@ function useContactCapture() {
|
|
|
499
978
|
};
|
|
500
979
|
}
|
|
501
980
|
|
|
981
|
+
// src/hooks/useSiteContent.ts
|
|
982
|
+
var import_react12 = require("react");
|
|
983
|
+
function useSiteContent(contentKey, options = {}) {
|
|
984
|
+
const { defaultValue = "", fetchOnMount = true } = options;
|
|
985
|
+
const { client } = useFoxPixelContext();
|
|
986
|
+
const { user, hasPermission } = useAuth();
|
|
987
|
+
const [data, setData] = (0, import_react12.useState)(null);
|
|
988
|
+
const [isLoading, setIsLoading] = (0, import_react12.useState)(fetchOnMount);
|
|
989
|
+
const [error, setError] = (0, import_react12.useState)(null);
|
|
990
|
+
const canEdit = user !== null && hasPermission("site:content:update");
|
|
991
|
+
const fetchContent = (0, import_react12.useCallback)(async () => {
|
|
992
|
+
try {
|
|
993
|
+
setIsLoading(true);
|
|
994
|
+
setError(null);
|
|
995
|
+
const content = await client.get(
|
|
996
|
+
`/api/site/content/${encodeURIComponent(contentKey)}`
|
|
997
|
+
);
|
|
998
|
+
setData(content);
|
|
999
|
+
} catch (err) {
|
|
1000
|
+
if (err?.status === 404) {
|
|
1001
|
+
setData(null);
|
|
1002
|
+
} else {
|
|
1003
|
+
setError(err);
|
|
1004
|
+
}
|
|
1005
|
+
} finally {
|
|
1006
|
+
setIsLoading(false);
|
|
1007
|
+
}
|
|
1008
|
+
}, [client, contentKey]);
|
|
1009
|
+
const updateContent = (0, import_react12.useCallback)(async (newValue) => {
|
|
1010
|
+
try {
|
|
1011
|
+
setError(null);
|
|
1012
|
+
const updated = await client.put(
|
|
1013
|
+
`/api/site/content/${encodeURIComponent(contentKey)}`,
|
|
1014
|
+
{ value: newValue }
|
|
1015
|
+
);
|
|
1016
|
+
setData(updated);
|
|
1017
|
+
} catch (err) {
|
|
1018
|
+
setError(err);
|
|
1019
|
+
throw err;
|
|
1020
|
+
}
|
|
1021
|
+
}, [client, contentKey]);
|
|
1022
|
+
(0, import_react12.useEffect)(() => {
|
|
1023
|
+
if (fetchOnMount) {
|
|
1024
|
+
fetchContent();
|
|
1025
|
+
}
|
|
1026
|
+
}, [contentKey, fetchOnMount]);
|
|
1027
|
+
const value = data?.value ?? defaultValue;
|
|
1028
|
+
return {
|
|
1029
|
+
data,
|
|
1030
|
+
value,
|
|
1031
|
+
isLoading,
|
|
1032
|
+
error,
|
|
1033
|
+
canEdit,
|
|
1034
|
+
update: updateContent,
|
|
1035
|
+
refetch: fetchContent
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
function useSiteContents(contentKeys, options = {}) {
|
|
1039
|
+
const { defaults = {} } = options;
|
|
1040
|
+
const { client } = useFoxPixelContext();
|
|
1041
|
+
const [data, setData] = (0, import_react12.useState)({});
|
|
1042
|
+
const [isLoading, setIsLoading] = (0, import_react12.useState)(true);
|
|
1043
|
+
const [error, setError] = (0, import_react12.useState)(null);
|
|
1044
|
+
const fetchContents = (0, import_react12.useCallback)(async () => {
|
|
1045
|
+
if (contentKeys.length === 0) {
|
|
1046
|
+
setData({});
|
|
1047
|
+
setIsLoading(false);
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
try {
|
|
1051
|
+
setIsLoading(true);
|
|
1052
|
+
setError(null);
|
|
1053
|
+
const contents = await client.post(
|
|
1054
|
+
"/api/site/content/batch",
|
|
1055
|
+
contentKeys
|
|
1056
|
+
);
|
|
1057
|
+
setData(contents);
|
|
1058
|
+
} catch (err) {
|
|
1059
|
+
setError(err);
|
|
1060
|
+
} finally {
|
|
1061
|
+
setIsLoading(false);
|
|
1062
|
+
}
|
|
1063
|
+
}, [client, contentKeys.join(",")]);
|
|
1064
|
+
(0, import_react12.useEffect)(() => {
|
|
1065
|
+
fetchContents();
|
|
1066
|
+
}, [fetchContents]);
|
|
1067
|
+
const getValue = (0, import_react12.useCallback)((key, defaultValue) => {
|
|
1068
|
+
const content = data[key];
|
|
1069
|
+
if (content?.value) {
|
|
1070
|
+
return content.value;
|
|
1071
|
+
}
|
|
1072
|
+
return defaultValue ?? defaults[key] ?? "";
|
|
1073
|
+
}, [data, defaults]);
|
|
1074
|
+
return {
|
|
1075
|
+
data,
|
|
1076
|
+
getValue,
|
|
1077
|
+
isLoading,
|
|
1078
|
+
error,
|
|
1079
|
+
refetch: fetchContents
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
function useSiteContentSection(section) {
|
|
1083
|
+
const { client } = useFoxPixelContext();
|
|
1084
|
+
const [contents, setContents] = (0, import_react12.useState)([]);
|
|
1085
|
+
const [isLoading, setIsLoading] = (0, import_react12.useState)(true);
|
|
1086
|
+
const [error, setError] = (0, import_react12.useState)(null);
|
|
1087
|
+
const fetchContents = (0, import_react12.useCallback)(async () => {
|
|
1088
|
+
try {
|
|
1089
|
+
setIsLoading(true);
|
|
1090
|
+
setError(null);
|
|
1091
|
+
const data = await client.get(
|
|
1092
|
+
`/api/site/content/section/${encodeURIComponent(section)}`
|
|
1093
|
+
);
|
|
1094
|
+
setContents(data);
|
|
1095
|
+
} catch (err) {
|
|
1096
|
+
setError(err);
|
|
1097
|
+
} finally {
|
|
1098
|
+
setIsLoading(false);
|
|
1099
|
+
}
|
|
1100
|
+
}, [client, section]);
|
|
1101
|
+
(0, import_react12.useEffect)(() => {
|
|
1102
|
+
fetchContents();
|
|
1103
|
+
}, [fetchContents]);
|
|
1104
|
+
return {
|
|
1105
|
+
contents,
|
|
1106
|
+
isLoading,
|
|
1107
|
+
error,
|
|
1108
|
+
refetch: fetchContents
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// src/prefetchSiteContent.ts
|
|
1113
|
+
async function prefetchSiteContent(queryClient, options) {
|
|
1114
|
+
const { apiUrl, apiKey, tenantId, contentKeys } = options;
|
|
1115
|
+
const client = new FoxPixelHttpClient({ apiUrl, apiKey, tenantId });
|
|
1116
|
+
await Promise.all(
|
|
1117
|
+
contentKeys.map(async (contentKey) => {
|
|
1118
|
+
try {
|
|
1119
|
+
const content = await client.get(
|
|
1120
|
+
`/api/site/content/${encodeURIComponent(contentKey)}`
|
|
1121
|
+
);
|
|
1122
|
+
queryClient.setQueryData(
|
|
1123
|
+
[SITE_CONTENT_QUERY_KEY, contentKey],
|
|
1124
|
+
{
|
|
1125
|
+
value: content?.value ?? "",
|
|
1126
|
+
contentType: content?.contentType ?? "TEXT"
|
|
1127
|
+
}
|
|
1128
|
+
);
|
|
1129
|
+
} catch (err) {
|
|
1130
|
+
const status = err?.response?.status;
|
|
1131
|
+
if (status === 404) {
|
|
1132
|
+
queryClient.setQueryData([SITE_CONTENT_QUERY_KEY, contentKey], {
|
|
1133
|
+
value: "",
|
|
1134
|
+
contentType: "TEXT"
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
})
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
502
1142
|
// src/blog/hooks.ts
|
|
503
|
-
var
|
|
1143
|
+
var import_react13 = require("react");
|
|
504
1144
|
function useBlogPosts(options = {}) {
|
|
505
1145
|
const { client } = useFoxPixelContext();
|
|
506
|
-
const [data, setData] = (0,
|
|
507
|
-
const [isLoading, setIsLoading] = (0,
|
|
508
|
-
const [error, setError] = (0,
|
|
1146
|
+
const [data, setData] = (0, import_react13.useState)(null);
|
|
1147
|
+
const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
|
|
1148
|
+
const [error, setError] = (0, import_react13.useState)(null);
|
|
509
1149
|
const page = options.page ?? 0;
|
|
510
1150
|
const limit = options.limit ?? 10;
|
|
511
1151
|
const fetchPosts = async () => {
|
|
@@ -525,7 +1165,7 @@ function useBlogPosts(options = {}) {
|
|
|
525
1165
|
setIsLoading(false);
|
|
526
1166
|
}
|
|
527
1167
|
};
|
|
528
|
-
(0,
|
|
1168
|
+
(0, import_react13.useEffect)(() => {
|
|
529
1169
|
fetchPosts();
|
|
530
1170
|
}, [page, limit]);
|
|
531
1171
|
return {
|
|
@@ -537,9 +1177,9 @@ function useBlogPosts(options = {}) {
|
|
|
537
1177
|
}
|
|
538
1178
|
function useBlogPost(slug) {
|
|
539
1179
|
const { client } = useFoxPixelContext();
|
|
540
|
-
const [data, setData] = (0,
|
|
541
|
-
const [isLoading, setIsLoading] = (0,
|
|
542
|
-
const [error, setError] = (0,
|
|
1180
|
+
const [data, setData] = (0, import_react13.useState)(null);
|
|
1181
|
+
const [isLoading, setIsLoading] = (0, import_react13.useState)(!!slug);
|
|
1182
|
+
const [error, setError] = (0, import_react13.useState)(null);
|
|
543
1183
|
const fetchPost = async () => {
|
|
544
1184
|
if (!slug) {
|
|
545
1185
|
setData(null);
|
|
@@ -558,7 +1198,7 @@ function useBlogPost(slug) {
|
|
|
558
1198
|
setIsLoading(false);
|
|
559
1199
|
}
|
|
560
1200
|
};
|
|
561
|
-
(0,
|
|
1201
|
+
(0, import_react13.useEffect)(() => {
|
|
562
1202
|
fetchPost();
|
|
563
1203
|
}, [slug]);
|
|
564
1204
|
return {
|
|
@@ -570,9 +1210,9 @@ function useBlogPost(slug) {
|
|
|
570
1210
|
}
|
|
571
1211
|
function useBlogCategories() {
|
|
572
1212
|
const { client } = useFoxPixelContext();
|
|
573
|
-
const [data, setData] = (0,
|
|
574
|
-
const [isLoading, setIsLoading] = (0,
|
|
575
|
-
const [error, setError] = (0,
|
|
1213
|
+
const [data, setData] = (0, import_react13.useState)(null);
|
|
1214
|
+
const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
|
|
1215
|
+
const [error, setError] = (0, import_react13.useState)(null);
|
|
576
1216
|
const fetchCategories = async () => {
|
|
577
1217
|
try {
|
|
578
1218
|
setIsLoading(true);
|
|
@@ -586,7 +1226,7 @@ function useBlogCategories() {
|
|
|
586
1226
|
setIsLoading(false);
|
|
587
1227
|
}
|
|
588
1228
|
};
|
|
589
|
-
(0,
|
|
1229
|
+
(0, import_react13.useEffect)(() => {
|
|
590
1230
|
fetchCategories();
|
|
591
1231
|
}, []);
|
|
592
1232
|
return {
|
|
@@ -598,9 +1238,9 @@ function useBlogCategories() {
|
|
|
598
1238
|
}
|
|
599
1239
|
function useBlogTags() {
|
|
600
1240
|
const { client } = useFoxPixelContext();
|
|
601
|
-
const [data, setData] = (0,
|
|
602
|
-
const [isLoading, setIsLoading] = (0,
|
|
603
|
-
const [error, setError] = (0,
|
|
1241
|
+
const [data, setData] = (0, import_react13.useState)(null);
|
|
1242
|
+
const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
|
|
1243
|
+
const [error, setError] = (0, import_react13.useState)(null);
|
|
604
1244
|
const fetchTags = async () => {
|
|
605
1245
|
try {
|
|
606
1246
|
setIsLoading(true);
|
|
@@ -614,7 +1254,7 @@ function useBlogTags() {
|
|
|
614
1254
|
setIsLoading(false);
|
|
615
1255
|
}
|
|
616
1256
|
};
|
|
617
|
-
(0,
|
|
1257
|
+
(0, import_react13.useEffect)(() => {
|
|
618
1258
|
fetchTags();
|
|
619
1259
|
}, []);
|
|
620
1260
|
return {
|
|
@@ -626,9 +1266,9 @@ function useBlogTags() {
|
|
|
626
1266
|
}
|
|
627
1267
|
function useBlogComments(slug) {
|
|
628
1268
|
const { client } = useFoxPixelContext();
|
|
629
|
-
const [data, setData] = (0,
|
|
630
|
-
const [isLoading, setIsLoading] = (0,
|
|
631
|
-
const [error, setError] = (0,
|
|
1269
|
+
const [data, setData] = (0, import_react13.useState)(null);
|
|
1270
|
+
const [isLoading, setIsLoading] = (0, import_react13.useState)(!!slug);
|
|
1271
|
+
const [error, setError] = (0, import_react13.useState)(null);
|
|
632
1272
|
const fetchComments = async () => {
|
|
633
1273
|
if (!slug) {
|
|
634
1274
|
setData(null);
|
|
@@ -649,7 +1289,7 @@ function useBlogComments(slug) {
|
|
|
649
1289
|
setIsLoading(false);
|
|
650
1290
|
}
|
|
651
1291
|
};
|
|
652
|
-
(0,
|
|
1292
|
+
(0, import_react13.useEffect)(() => {
|
|
653
1293
|
fetchComments();
|
|
654
1294
|
}, [slug]);
|
|
655
1295
|
return {
|
|
@@ -661,8 +1301,8 @@ function useBlogComments(slug) {
|
|
|
661
1301
|
}
|
|
662
1302
|
function useBlogCommentSubmit(slug) {
|
|
663
1303
|
const { client } = useFoxPixelContext();
|
|
664
|
-
const [isSubmitting, setIsSubmitting] = (0,
|
|
665
|
-
const [error, setError] = (0,
|
|
1304
|
+
const [isSubmitting, setIsSubmitting] = (0, import_react13.useState)(false);
|
|
1305
|
+
const [error, setError] = (0, import_react13.useState)(null);
|
|
666
1306
|
const submit = async (payload) => {
|
|
667
1307
|
if (!slug) return null;
|
|
668
1308
|
try {
|
|
@@ -690,9 +1330,9 @@ function useBlogCommentSubmit(slug) {
|
|
|
690
1330
|
}
|
|
691
1331
|
function useBlogFeaturedPosts(limit = 6) {
|
|
692
1332
|
const { client } = useFoxPixelContext();
|
|
693
|
-
const [data, setData] = (0,
|
|
694
|
-
const [isLoading, setIsLoading] = (0,
|
|
695
|
-
const [error, setError] = (0,
|
|
1333
|
+
const [data, setData] = (0, import_react13.useState)(null);
|
|
1334
|
+
const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
|
|
1335
|
+
const [error, setError] = (0, import_react13.useState)(null);
|
|
696
1336
|
const fetchFeatured = async () => {
|
|
697
1337
|
try {
|
|
698
1338
|
setIsLoading(true);
|
|
@@ -710,7 +1350,7 @@ function useBlogFeaturedPosts(limit = 6) {
|
|
|
710
1350
|
setIsLoading(false);
|
|
711
1351
|
}
|
|
712
1352
|
};
|
|
713
|
-
(0,
|
|
1353
|
+
(0, import_react13.useEffect)(() => {
|
|
714
1354
|
fetchFeatured();
|
|
715
1355
|
}, [limit]);
|
|
716
1356
|
return {
|
|
@@ -722,9 +1362,9 @@ function useBlogFeaturedPosts(limit = 6) {
|
|
|
722
1362
|
}
|
|
723
1363
|
function useNewsletterSubscribe() {
|
|
724
1364
|
const { client } = useFoxPixelContext();
|
|
725
|
-
const [isSubmitting, setIsSubmitting] = (0,
|
|
726
|
-
const [error, setError] = (0,
|
|
727
|
-
const [success, setSuccess] = (0,
|
|
1365
|
+
const [isSubmitting, setIsSubmitting] = (0, import_react13.useState)(false);
|
|
1366
|
+
const [error, setError] = (0, import_react13.useState)(null);
|
|
1367
|
+
const [success, setSuccess] = (0, import_react13.useState)(false);
|
|
728
1368
|
const subscribe = async (payload) => {
|
|
729
1369
|
try {
|
|
730
1370
|
setIsSubmitting(true);
|
|
@@ -757,9 +1397,9 @@ function useNewsletterSubscribe() {
|
|
|
757
1397
|
}
|
|
758
1398
|
function useNewsletterUnsubscribe() {
|
|
759
1399
|
const { client } = useFoxPixelContext();
|
|
760
|
-
const [isSubmitting, setIsSubmitting] = (0,
|
|
761
|
-
const [error, setError] = (0,
|
|
762
|
-
const [success, setSuccess] = (0,
|
|
1400
|
+
const [isSubmitting, setIsSubmitting] = (0, import_react13.useState)(false);
|
|
1401
|
+
const [error, setError] = (0, import_react13.useState)(null);
|
|
1402
|
+
const [success, setSuccess] = (0, import_react13.useState)(false);
|
|
763
1403
|
const unsubscribe = async (email) => {
|
|
764
1404
|
try {
|
|
765
1405
|
setIsSubmitting(true);
|
|
@@ -777,8 +1417,26 @@ function useNewsletterUnsubscribe() {
|
|
|
777
1417
|
setIsSubmitting(false);
|
|
778
1418
|
}
|
|
779
1419
|
};
|
|
1420
|
+
const unsubscribeByToken = async (token) => {
|
|
1421
|
+
try {
|
|
1422
|
+
setIsSubmitting(true);
|
|
1423
|
+
setError(null);
|
|
1424
|
+
setSuccess(false);
|
|
1425
|
+
await client.get("/api/v1/blog/newsletter/unsubscribe", {
|
|
1426
|
+
params: { token }
|
|
1427
|
+
});
|
|
1428
|
+
setSuccess(true);
|
|
1429
|
+
return true;
|
|
1430
|
+
} catch (err) {
|
|
1431
|
+
setError(err);
|
|
1432
|
+
return false;
|
|
1433
|
+
} finally {
|
|
1434
|
+
setIsSubmitting(false);
|
|
1435
|
+
}
|
|
1436
|
+
};
|
|
780
1437
|
return {
|
|
781
1438
|
unsubscribe,
|
|
1439
|
+
unsubscribeByToken,
|
|
782
1440
|
isSubmitting,
|
|
783
1441
|
error,
|
|
784
1442
|
success
|
|
@@ -786,15 +1444,15 @@ function useNewsletterUnsubscribe() {
|
|
|
786
1444
|
}
|
|
787
1445
|
|
|
788
1446
|
// src/blog/admin-hooks.ts
|
|
789
|
-
var
|
|
1447
|
+
var import_react14 = require("react");
|
|
790
1448
|
function useAdminBlogPosts(options = {}) {
|
|
791
1449
|
const { client } = useFoxPixelContext();
|
|
792
|
-
const [data, setData] = (0,
|
|
793
|
-
const [isLoading, setIsLoading] = (0,
|
|
794
|
-
const [error, setError] = (0,
|
|
1450
|
+
const [data, setData] = (0, import_react14.useState)(null);
|
|
1451
|
+
const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
|
|
1452
|
+
const [error, setError] = (0, import_react14.useState)(null);
|
|
795
1453
|
const page = options.page ?? 0;
|
|
796
1454
|
const size = options.size ?? 20;
|
|
797
|
-
const fetchPosts = (0,
|
|
1455
|
+
const fetchPosts = (0, import_react14.useCallback)(async () => {
|
|
798
1456
|
try {
|
|
799
1457
|
setIsLoading(true);
|
|
800
1458
|
setError(null);
|
|
@@ -809,17 +1467,17 @@ function useAdminBlogPosts(options = {}) {
|
|
|
809
1467
|
setIsLoading(false);
|
|
810
1468
|
}
|
|
811
1469
|
}, [client, page, size]);
|
|
812
|
-
(0,
|
|
1470
|
+
(0, import_react14.useEffect)(() => {
|
|
813
1471
|
fetchPosts();
|
|
814
1472
|
}, [fetchPosts]);
|
|
815
1473
|
return { data, isLoading, error, refetch: fetchPosts };
|
|
816
1474
|
}
|
|
817
1475
|
function useAdminBlogPost(id) {
|
|
818
1476
|
const { client } = useFoxPixelContext();
|
|
819
|
-
const [data, setData] = (0,
|
|
820
|
-
const [isLoading, setIsLoading] = (0,
|
|
821
|
-
const [error, setError] = (0,
|
|
822
|
-
const fetchPost = (0,
|
|
1477
|
+
const [data, setData] = (0, import_react14.useState)(null);
|
|
1478
|
+
const [isLoading, setIsLoading] = (0, import_react14.useState)(!!id);
|
|
1479
|
+
const [error, setError] = (0, import_react14.useState)(null);
|
|
1480
|
+
const fetchPost = (0, import_react14.useCallback)(async () => {
|
|
823
1481
|
if (!id) {
|
|
824
1482
|
setData(null);
|
|
825
1483
|
setIsLoading(false);
|
|
@@ -836,15 +1494,15 @@ function useAdminBlogPost(id) {
|
|
|
836
1494
|
setIsLoading(false);
|
|
837
1495
|
}
|
|
838
1496
|
}, [client, id]);
|
|
839
|
-
(0,
|
|
1497
|
+
(0, import_react14.useEffect)(() => {
|
|
840
1498
|
fetchPost();
|
|
841
1499
|
}, [fetchPost]);
|
|
842
1500
|
return { data, isLoading, error, refetch: fetchPost };
|
|
843
1501
|
}
|
|
844
1502
|
function useAdminBlogPostMutations() {
|
|
845
1503
|
const { client } = useFoxPixelContext();
|
|
846
|
-
const [isLoading, setIsLoading] = (0,
|
|
847
|
-
const [error, setError] = (0,
|
|
1504
|
+
const [isLoading, setIsLoading] = (0, import_react14.useState)(false);
|
|
1505
|
+
const [error, setError] = (0, import_react14.useState)(null);
|
|
848
1506
|
const create = async (payload) => {
|
|
849
1507
|
try {
|
|
850
1508
|
setIsLoading(true);
|
|
@@ -888,10 +1546,10 @@ function useAdminBlogPostMutations() {
|
|
|
888
1546
|
}
|
|
889
1547
|
function useAdminBlogCategories() {
|
|
890
1548
|
const { client } = useFoxPixelContext();
|
|
891
|
-
const [data, setData] = (0,
|
|
892
|
-
const [isLoading, setIsLoading] = (0,
|
|
893
|
-
const [error, setError] = (0,
|
|
894
|
-
const fetchCategories = (0,
|
|
1549
|
+
const [data, setData] = (0, import_react14.useState)(null);
|
|
1550
|
+
const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
|
|
1551
|
+
const [error, setError] = (0, import_react14.useState)(null);
|
|
1552
|
+
const fetchCategories = (0, import_react14.useCallback)(async () => {
|
|
895
1553
|
try {
|
|
896
1554
|
setIsLoading(true);
|
|
897
1555
|
setError(null);
|
|
@@ -903,7 +1561,7 @@ function useAdminBlogCategories() {
|
|
|
903
1561
|
setIsLoading(false);
|
|
904
1562
|
}
|
|
905
1563
|
}, [client]);
|
|
906
|
-
(0,
|
|
1564
|
+
(0, import_react14.useEffect)(() => {
|
|
907
1565
|
fetchCategories();
|
|
908
1566
|
}, [fetchCategories]);
|
|
909
1567
|
const create = async (payload) => {
|
|
@@ -940,10 +1598,10 @@ function useAdminBlogCategories() {
|
|
|
940
1598
|
}
|
|
941
1599
|
function useAdminBlogTags() {
|
|
942
1600
|
const { client } = useFoxPixelContext();
|
|
943
|
-
const [data, setData] = (0,
|
|
944
|
-
const [isLoading, setIsLoading] = (0,
|
|
945
|
-
const [error, setError] = (0,
|
|
946
|
-
const fetchTags = (0,
|
|
1601
|
+
const [data, setData] = (0, import_react14.useState)(null);
|
|
1602
|
+
const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
|
|
1603
|
+
const [error, setError] = (0, import_react14.useState)(null);
|
|
1604
|
+
const fetchTags = (0, import_react14.useCallback)(async () => {
|
|
947
1605
|
try {
|
|
948
1606
|
setIsLoading(true);
|
|
949
1607
|
setError(null);
|
|
@@ -955,7 +1613,7 @@ function useAdminBlogTags() {
|
|
|
955
1613
|
setIsLoading(false);
|
|
956
1614
|
}
|
|
957
1615
|
}, [client]);
|
|
958
|
-
(0,
|
|
1616
|
+
(0, import_react14.useEffect)(() => {
|
|
959
1617
|
fetchTags();
|
|
960
1618
|
}, [fetchTags]);
|
|
961
1619
|
const create = async (payload) => {
|
|
@@ -992,11 +1650,11 @@ function useAdminBlogTags() {
|
|
|
992
1650
|
}
|
|
993
1651
|
function useAdminBlogComments(options = {}) {
|
|
994
1652
|
const { client } = useFoxPixelContext();
|
|
995
|
-
const [data, setData] = (0,
|
|
996
|
-
const [isLoading, setIsLoading] = (0,
|
|
997
|
-
const [error, setError] = (0,
|
|
1653
|
+
const [data, setData] = (0, import_react14.useState)(null);
|
|
1654
|
+
const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
|
|
1655
|
+
const [error, setError] = (0, import_react14.useState)(null);
|
|
998
1656
|
const { status, postId, page = 0, size = 20 } = options;
|
|
999
|
-
const fetchComments = (0,
|
|
1657
|
+
const fetchComments = (0, import_react14.useCallback)(async () => {
|
|
1000
1658
|
try {
|
|
1001
1659
|
setIsLoading(true);
|
|
1002
1660
|
setError(null);
|
|
@@ -1013,7 +1671,7 @@ function useAdminBlogComments(options = {}) {
|
|
|
1013
1671
|
setIsLoading(false);
|
|
1014
1672
|
}
|
|
1015
1673
|
}, [client, status, postId, page, size]);
|
|
1016
|
-
(0,
|
|
1674
|
+
(0, import_react14.useEffect)(() => {
|
|
1017
1675
|
fetchComments();
|
|
1018
1676
|
}, [fetchComments]);
|
|
1019
1677
|
const updateStatus = async (id, newStatus) => {
|
|
@@ -1040,11 +1698,11 @@ function useAdminBlogComments(options = {}) {
|
|
|
1040
1698
|
}
|
|
1041
1699
|
function useAdminNewsletterSubscribers(options = {}) {
|
|
1042
1700
|
const { client } = useFoxPixelContext();
|
|
1043
|
-
const [data, setData] = (0,
|
|
1044
|
-
const [isLoading, setIsLoading] = (0,
|
|
1045
|
-
const [error, setError] = (0,
|
|
1701
|
+
const [data, setData] = (0, import_react14.useState)(null);
|
|
1702
|
+
const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
|
|
1703
|
+
const [error, setError] = (0, import_react14.useState)(null);
|
|
1046
1704
|
const { status, page = 0, size = 20 } = options;
|
|
1047
|
-
const fetchSubscribers = (0,
|
|
1705
|
+
const fetchSubscribers = (0, import_react14.useCallback)(async () => {
|
|
1048
1706
|
try {
|
|
1049
1707
|
setIsLoading(true);
|
|
1050
1708
|
setError(null);
|
|
@@ -1060,7 +1718,7 @@ function useAdminNewsletterSubscribers(options = {}) {
|
|
|
1060
1718
|
setIsLoading(false);
|
|
1061
1719
|
}
|
|
1062
1720
|
}, [client, status, page, size]);
|
|
1063
|
-
(0,
|
|
1721
|
+
(0, import_react14.useEffect)(() => {
|
|
1064
1722
|
fetchSubscribers();
|
|
1065
1723
|
}, [fetchSubscribers]);
|
|
1066
1724
|
const remove = async (id) => {
|
|
@@ -1077,10 +1735,10 @@ function useAdminNewsletterSubscribers(options = {}) {
|
|
|
1077
1735
|
}
|
|
1078
1736
|
function useAdminNewsletterStats() {
|
|
1079
1737
|
const { client } = useFoxPixelContext();
|
|
1080
|
-
const [data, setData] = (0,
|
|
1081
|
-
const [isLoading, setIsLoading] = (0,
|
|
1082
|
-
const [error, setError] = (0,
|
|
1083
|
-
const fetchStats = (0,
|
|
1738
|
+
const [data, setData] = (0, import_react14.useState)(null);
|
|
1739
|
+
const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
|
|
1740
|
+
const [error, setError] = (0, import_react14.useState)(null);
|
|
1741
|
+
const fetchStats = (0, import_react14.useCallback)(async () => {
|
|
1084
1742
|
try {
|
|
1085
1743
|
setIsLoading(true);
|
|
1086
1744
|
setError(null);
|
|
@@ -1092,17 +1750,17 @@ function useAdminNewsletterStats() {
|
|
|
1092
1750
|
setIsLoading(false);
|
|
1093
1751
|
}
|
|
1094
1752
|
}, [client]);
|
|
1095
|
-
(0,
|
|
1753
|
+
(0, import_react14.useEffect)(() => {
|
|
1096
1754
|
fetchStats();
|
|
1097
1755
|
}, [fetchStats]);
|
|
1098
1756
|
return { data, isLoading, error, refetch: fetchStats };
|
|
1099
1757
|
}
|
|
1100
1758
|
function useAdminBlogSettings() {
|
|
1101
1759
|
const { client } = useFoxPixelContext();
|
|
1102
|
-
const [data, setData] = (0,
|
|
1103
|
-
const [isLoading, setIsLoading] = (0,
|
|
1104
|
-
const [error, setError] = (0,
|
|
1105
|
-
const fetchSettings = (0,
|
|
1760
|
+
const [data, setData] = (0, import_react14.useState)(null);
|
|
1761
|
+
const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
|
|
1762
|
+
const [error, setError] = (0, import_react14.useState)(null);
|
|
1763
|
+
const fetchSettings = (0, import_react14.useCallback)(async () => {
|
|
1106
1764
|
try {
|
|
1107
1765
|
setIsLoading(true);
|
|
1108
1766
|
setError(null);
|
|
@@ -1114,7 +1772,7 @@ function useAdminBlogSettings() {
|
|
|
1114
1772
|
setIsLoading(false);
|
|
1115
1773
|
}
|
|
1116
1774
|
}, [client]);
|
|
1117
|
-
(0,
|
|
1775
|
+
(0, import_react14.useEffect)(() => {
|
|
1118
1776
|
fetchSettings();
|
|
1119
1777
|
}, [fetchSettings]);
|
|
1120
1778
|
const update = async (settings) => {
|
|
@@ -1131,10 +1789,10 @@ function useAdminBlogSettings() {
|
|
|
1131
1789
|
}
|
|
1132
1790
|
function useAdminBlogAnalytics() {
|
|
1133
1791
|
const { client } = useFoxPixelContext();
|
|
1134
|
-
const [data, setData] = (0,
|
|
1135
|
-
const [isLoading, setIsLoading] = (0,
|
|
1136
|
-
const [error, setError] = (0,
|
|
1137
|
-
const fetchAnalytics = (0,
|
|
1792
|
+
const [data, setData] = (0, import_react14.useState)(null);
|
|
1793
|
+
const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
|
|
1794
|
+
const [error, setError] = (0, import_react14.useState)(null);
|
|
1795
|
+
const fetchAnalytics = (0, import_react14.useCallback)(async () => {
|
|
1138
1796
|
try {
|
|
1139
1797
|
setIsLoading(true);
|
|
1140
1798
|
setError(null);
|
|
@@ -1146,7 +1804,7 @@ function useAdminBlogAnalytics() {
|
|
|
1146
1804
|
setIsLoading(false);
|
|
1147
1805
|
}
|
|
1148
1806
|
}, [client]);
|
|
1149
|
-
(0,
|
|
1807
|
+
(0, import_react14.useEffect)(() => {
|
|
1150
1808
|
fetchAnalytics();
|
|
1151
1809
|
}, [fetchAnalytics]);
|
|
1152
1810
|
return { data, isLoading, error, refetch: fetchAnalytics };
|
|
@@ -1186,11 +1844,16 @@ function getBlogPostSchemaLd(post, options) {
|
|
|
1186
1844
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1187
1845
|
0 && (module.exports = {
|
|
1188
1846
|
AuthProvider,
|
|
1847
|
+
Editable,
|
|
1848
|
+
EditableHTML,
|
|
1849
|
+
EditableImage,
|
|
1189
1850
|
FoxPixelHttpClient,
|
|
1190
1851
|
FoxPixelProvider,
|
|
1191
1852
|
GuestOnlyRoute,
|
|
1192
1853
|
ProtectedRoute,
|
|
1854
|
+
SITE_CONTENT_QUERY_KEY,
|
|
1193
1855
|
getBlogPostSchemaLd,
|
|
1856
|
+
prefetchSiteContent,
|
|
1194
1857
|
useAdminBlogAnalytics,
|
|
1195
1858
|
useAdminBlogCategories,
|
|
1196
1859
|
useAdminBlogComments,
|
|
@@ -1210,11 +1873,18 @@ function getBlogPostSchemaLd(post, options) {
|
|
|
1210
1873
|
useBlogPosts,
|
|
1211
1874
|
useBlogTags,
|
|
1212
1875
|
useContactCapture,
|
|
1876
|
+
useEditMode,
|
|
1877
|
+
useEditModeMessaging,
|
|
1213
1878
|
useFoxPixelContext,
|
|
1214
1879
|
useLeadCapture,
|
|
1215
1880
|
useNewsletterSubscribe,
|
|
1216
1881
|
useNewsletterUnsubscribe,
|
|
1882
|
+
useSendEditRequest,
|
|
1217
1883
|
useServices,
|
|
1884
|
+
useSiteContent,
|
|
1885
|
+
useSiteContentQuery,
|
|
1886
|
+
useSiteContentSection,
|
|
1887
|
+
useSiteContents,
|
|
1218
1888
|
withAuth
|
|
1219
1889
|
});
|
|
1220
1890
|
//# sourceMappingURL=index.js.map
|