@foxpixel/react 0.2.0 → 0.2.2

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.js CHANGED
@@ -40,6 +40,7 @@ __export(index_exports, {
40
40
  ProtectedRoute: () => ProtectedRoute,
41
41
  SITE_CONTENT_QUERY_KEY: () => SITE_CONTENT_QUERY_KEY,
42
42
  getBlogPostSchemaLd: () => getBlogPostSchemaLd,
43
+ prefetchSiteContent: () => prefetchSiteContent,
43
44
  useAdminBlogAnalytics: () => useAdminBlogAnalytics,
44
45
  useAdminBlogCategories: () => useAdminBlogCategories,
45
46
  useAdminBlogComments: () => useAdminBlogComments,
@@ -76,7 +77,7 @@ __export(index_exports, {
76
77
  module.exports = __toCommonJS(index_exports);
77
78
 
78
79
  // src/context/FoxPixelContext.tsx
79
- var import_react = require("react");
80
+ var import_react = __toESM(require("react"));
80
81
 
81
82
  // src/client/http.ts
82
83
  var import_axios = __toESM(require("axios"));
@@ -188,15 +189,21 @@ var FoxPixelHttpClient = class {
188
189
 
189
190
  // src/context/FoxPixelContext.tsx
190
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
+ }
191
197
  var FoxPixelContext = (0, import_react.createContext)(null);
192
- function FoxPixelProvider({ children, config = {} }) {
198
+ function FoxPixelProvider({ children, config = {}, queryClient }) {
193
199
  const client = (0, import_react.useMemo)(() => {
194
200
  return new FoxPixelHttpClient(config);
195
201
  }, [config.apiUrl, config.apiKey, config.tenantId]);
196
202
  const value = (0, import_react.useMemo)(() => ({
197
203
  client,
198
- config
199
- }), [client, config]);
204
+ config,
205
+ queryClient: queryClient ?? null
206
+ }), [client, config, queryClient]);
200
207
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FoxPixelContext.Provider, { value, children });
201
208
  }
202
209
  function useFoxPixelContext() {
@@ -423,12 +430,33 @@ function withAuth(Component, options = {}) {
423
430
  }
424
431
 
425
432
  // src/components/Editable.tsx
426
- var import_react7 = require("react");
433
+ var import_react8 = require("react");
427
434
 
428
435
  // src/hooks/useEditMode.ts
429
436
  var import_react6 = require("react");
430
- var import_react_query = require("@tanstack/react-query");
431
437
  var SITE_CONTENT_QUERY_KEY = "siteContent";
438
+ var contentKeysOnPage = /* @__PURE__ */ new Set();
439
+ var flushTimer = null;
440
+ function scheduleFlushContentKeys() {
441
+ if (flushTimer) return;
442
+ flushTimer = setTimeout(() => {
443
+ flushTimer = null;
444
+ if (typeof window !== "undefined" && window.parent !== window) {
445
+ window.parent.postMessage(
446
+ { type: "FOXPIXEL_READY", payload: { contentKeys: Array.from(contentKeysOnPage) } },
447
+ "*"
448
+ );
449
+ }
450
+ }, 150);
451
+ }
452
+ function registerContentKey(key) {
453
+ contentKeysOnPage.add(key);
454
+ scheduleFlushContentKeys();
455
+ }
456
+ function unregisterContentKey(key) {
457
+ contentKeysOnPage.delete(key);
458
+ scheduleFlushContentKeys();
459
+ }
432
460
  function useEditMode() {
433
461
  const [isEditMode, setIsEditMode] = (0, import_react6.useState)(false);
434
462
  (0, import_react6.useEffect)(() => {
@@ -439,18 +467,29 @@ function useEditMode() {
439
467
  return isEditMode;
440
468
  }
441
469
  function useEditModeMessaging() {
442
- const queryClient = (0, import_react_query.useQueryClient)();
470
+ const ctx = useFoxPixelContext();
471
+ const queryClient = ctx?.queryClient ?? null;
443
472
  const isEditMode = useEditMode();
444
473
  (0, import_react6.useEffect)(() => {
445
474
  if (!isEditMode || typeof window === "undefined") return;
446
475
  window.parent.postMessage({ type: "FOXPIXEL_READY" }, "*");
447
476
  const handleMessage = (event) => {
477
+ if (!queryClient) return;
448
478
  const { type, payload } = event.data || {};
449
- if (type === "FOXPIXEL_CONTENT_UPDATED" && payload?.contentKey) {
450
- queryClient.invalidateQueries({
451
- queryKey: [SITE_CONTENT_QUERY_KEY, payload.contentKey]
452
- });
479
+ if (type !== "FOXPIXEL_CONTENT_UPDATED" || !payload?.contentKey) return;
480
+ const { contentKey, newValue } = payload;
481
+ if (typeof newValue === "string") {
482
+ queryClient.setQueryData(
483
+ [SITE_CONTENT_QUERY_KEY, contentKey],
484
+ (prev) => ({
485
+ value: newValue,
486
+ contentType: prev?.contentType ?? "TEXT"
487
+ })
488
+ );
453
489
  }
490
+ queryClient.invalidateQueries({
491
+ queryKey: [SITE_CONTENT_QUERY_KEY, contentKey]
492
+ });
454
493
  };
455
494
  window.addEventListener("message", handleMessage);
456
495
  return () => window.removeEventListener("message", handleMessage);
@@ -483,16 +522,40 @@ function useSendEditRequest() {
483
522
  }
484
523
 
485
524
  // src/hooks/useSiteContentQuery.ts
486
- var import_react_query2 = require("@tanstack/react-query");
525
+ var import_react7 = require("react");
526
+ function getCached(queryClient, contentKey) {
527
+ const data = queryClient.getQueryData([
528
+ SITE_CONTENT_QUERY_KEY,
529
+ contentKey
530
+ ]);
531
+ if (data == null) return void 0;
532
+ return { value: data.value ?? "", contentType: data.contentType ?? "TEXT" };
533
+ }
487
534
  function useSiteContentQuery(contentKey, options) {
488
535
  const { defaultValue } = options;
489
- const { client } = useFoxPixelContext();
490
- const { data, isLoading } = (0, import_react_query2.useQuery)({
491
- queryKey: [SITE_CONTENT_QUERY_KEY, contentKey],
492
- queryFn: async () => {
536
+ const { client, queryClient } = useFoxPixelContext();
537
+ const [state, setState] = (0, import_react7.useState)(() => {
538
+ if (queryClient) {
539
+ const cached = getCached(queryClient, contentKey);
540
+ if (cached) {
541
+ return { value: cached.value, isLoading: false, contentType: cached.contentType };
542
+ }
543
+ }
544
+ return { value: defaultValue, isLoading: true, contentType: "TEXT" };
545
+ });
546
+ const contentKeyRef = (0, import_react7.useRef)(contentKey);
547
+ contentKeyRef.current = contentKey;
548
+ (0, import_react7.useEffect)(() => {
549
+ if (!queryClient) {
550
+ setState((s) => ({ ...s, value: defaultValue, isLoading: false }));
551
+ return;
552
+ }
553
+ const key = contentKeyRef.current;
554
+ const queryKey = [SITE_CONTENT_QUERY_KEY, key];
555
+ const queryFn = async () => {
493
556
  try {
494
557
  const content = await client.get(
495
- `/api/site/content/${encodeURIComponent(contentKey)}`
558
+ `/api/site/content/${encodeURIComponent(key)}`
496
559
  );
497
560
  if (!content) return null;
498
561
  return {
@@ -500,23 +563,50 @@ function useSiteContentQuery(contentKey, options) {
500
563
  contentType: content.contentType ?? "TEXT"
501
564
  };
502
565
  } catch (err) {
503
- const status = err?.status;
566
+ const status = err?.response?.status;
504
567
  if (status === 404) return null;
505
568
  throw err;
506
569
  }
507
- },
508
- staleTime: 1e3 * 60 * 5,
509
- retry: 1
510
- });
511
- return {
512
- value: data?.value ?? defaultValue,
513
- isLoading,
514
- contentType: data?.contentType ?? "TEXT"
515
- };
570
+ };
571
+ let cancelled = false;
572
+ queryClient.fetchQuery({
573
+ queryKey,
574
+ queryFn,
575
+ staleTime: 1e3 * 60 * 5,
576
+ retry: 1
577
+ }).then((data) => {
578
+ if (cancelled) return;
579
+ setState({
580
+ value: data?.value ?? defaultValue,
581
+ isLoading: false,
582
+ contentType: data?.contentType ?? "TEXT"
583
+ });
584
+ }).catch(() => {
585
+ if (cancelled) return;
586
+ setState((s) => ({ ...s, value: defaultValue, isLoading: false }));
587
+ });
588
+ const unsub = queryClient.getQueryCache().subscribe((event) => {
589
+ if (event?.type === "updated" && event?.query?.queryKey[1] === key) {
590
+ const cached = getCached(queryClient, key);
591
+ if (cached && !cancelled) {
592
+ setState({
593
+ value: cached.value,
594
+ isLoading: false,
595
+ contentType: cached.contentType
596
+ });
597
+ }
598
+ }
599
+ });
600
+ return () => {
601
+ cancelled = true;
602
+ unsub();
603
+ };
604
+ }, [queryClient, contentKey, defaultValue, client]);
605
+ return state;
516
606
  }
517
607
 
518
608
  // src/utils/sanitize.ts
519
- var import_isomorphic_dompurify = __toESM(require("isomorphic-dompurify"));
609
+ var import_sanitize_html = __toESM(require("sanitize-html"));
520
610
  var DEFAULT_ALLOWED_TAGS = [
521
611
  "p",
522
612
  "br",
@@ -547,13 +637,16 @@ var DEFAULT_ALLOWED_TAGS = [
547
637
  "th",
548
638
  "td"
549
639
  ];
550
- var DEFAULT_ALLOWED_ATTR = ["href", "target", "rel", "src", "alt", "title", "class"];
640
+ var DEFAULT_ALLOWED_ATTR = {
641
+ a: ["href", "target", "rel", "title"],
642
+ img: ["src", "alt", "title", "width", "height"],
643
+ "*": ["class"]
644
+ };
551
645
  function sanitizeHtml(html) {
552
646
  if (typeof html !== "string") return "";
553
- return import_isomorphic_dompurify.default.sanitize(html, {
554
- ALLOWED_TAGS: DEFAULT_ALLOWED_TAGS,
555
- ALLOWED_ATTR: DEFAULT_ALLOWED_ATTR,
556
- ADD_ATTR: ["target"]
647
+ return (0, import_sanitize_html.default)(html, {
648
+ allowedTags: DEFAULT_ALLOWED_TAGS,
649
+ allowedAttributes: DEFAULT_ALLOWED_ATTR
557
650
  });
558
651
  }
559
652
 
@@ -564,6 +657,23 @@ function cn(...args) {
564
657
 
565
658
  // src/components/Editable.tsx
566
659
  var import_jsx_runtime6 = require("react/jsx-runtime");
660
+ var EDIT_MODE_TOOLTIP_STYLE = {
661
+ position: "absolute",
662
+ top: "-28px",
663
+ left: "50%",
664
+ transform: "translateX(-50%)",
665
+ fontSize: "10px",
666
+ fontFamily: "system-ui, sans-serif",
667
+ padding: "4px 8px",
668
+ backgroundColor: "rgb(37 99 235)",
669
+ color: "white",
670
+ borderRadius: "4px",
671
+ whiteSpace: "nowrap",
672
+ pointerEvents: "none",
673
+ zIndex: 100,
674
+ boxShadow: "0 1px 3px rgba(0,0,0,0.2)",
675
+ transition: "opacity 0.15s ease"
676
+ };
567
677
  function Editable({
568
678
  contentKey,
569
679
  defaultValue,
@@ -571,13 +681,20 @@ function Editable({
571
681
  multiline = false,
572
682
  className
573
683
  }) {
684
+ const [isHovered, setIsHovered] = (0, import_react8.useState)(false);
574
685
  const isEditMode = useEditModeMessaging();
575
686
  const sendEditRequest = useSendEditRequest();
576
687
  const { value, isLoading, contentType } = useSiteContentQuery(contentKey, {
577
688
  defaultValue
578
689
  });
579
690
  const section = contentKey.includes(".") ? contentKey.split(".")[0] : void 0;
580
- const handleClick = (0, import_react7.useCallback)(
691
+ (0, import_react8.useEffect)(() => {
692
+ if (isEditMode) {
693
+ registerContentKey(contentKey);
694
+ return () => unregisterContentKey(contentKey);
695
+ }
696
+ }, [isEditMode, contentKey]);
697
+ const handleClick = (0, import_react8.useCallback)(
581
698
  (e) => {
582
699
  if (isEditMode) {
583
700
  e.preventDefault();
@@ -593,7 +710,7 @@ function Editable({
593
710
  [isEditMode, contentKey, value, contentType, section, sendEditRequest]
594
711
  );
595
712
  if (isLoading) {
596
- return (0, import_react7.createElement)(as, {
713
+ return (0, import_react8.createElement)(as, {
597
714
  className: cn(
598
715
  "animate-pulse bg-muted rounded",
599
716
  multiline ? "h-20" : "h-6",
@@ -604,36 +721,59 @@ function Editable({
604
721
  "aria-label": "Loading content..."
605
722
  });
606
723
  }
607
- const editModeStyles = isEditMode ? cn(
608
- "cursor-pointer transition-all duration-200",
609
- "hover:ring-2 hover:ring-blue-500 hover:ring-offset-2",
610
- "hover:bg-blue-50/50 dark:hover:bg-blue-950/30",
611
- "relative group"
612
- ) : "";
724
+ const editModeWrapperStyle = isEditMode ? {
725
+ position: "relative",
726
+ display: "inline",
727
+ cursor: "pointer",
728
+ borderRadius: "2px",
729
+ outline: isHovered ? "2px solid rgb(59 130 246)" : "none",
730
+ outlineOffset: "2px",
731
+ backgroundColor: isHovered ? "rgba(59 130 246 / 0.08)" : void 0
732
+ } : {};
733
+ const tooltipSpan = isEditMode ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
734
+ "span",
735
+ {
736
+ style: {
737
+ ...EDIT_MODE_TOOLTIP_STYLE,
738
+ opacity: isHovered ? 1 : 0
739
+ },
740
+ "aria-hidden": true,
741
+ children: "Click to edit"
742
+ }
743
+ ) : null;
744
+ const hoverHandlers = isEditMode ? {
745
+ onMouseEnter: () => setIsHovered(true),
746
+ onMouseLeave: () => setIsHovered(false)
747
+ } : {};
613
748
  if (multiline && value.includes("\n")) {
614
749
  const safeBr = sanitizeHtml(value.replace(/\n/g, "<br />"));
615
- return (0, import_react7.createElement)(as, {
616
- className: cn(className, editModeStyles),
617
- "data-content-key": contentKey,
618
- "data-editable": isEditMode ? "true" : void 0,
619
- onClick: isEditMode ? handleClick : void 0,
620
- dangerouslySetInnerHTML: { __html: safeBr },
621
- title: isEditMode ? "Click to edit" : void 0
622
- });
750
+ return (0, import_react8.createElement)(
751
+ "span",
752
+ { style: editModeWrapperStyle, ...hoverHandlers },
753
+ (0, import_react8.createElement)(as, {
754
+ className,
755
+ "data-content-key": contentKey,
756
+ "data-editable": isEditMode ? "true" : void 0,
757
+ onClick: isEditMode ? handleClick : void 0,
758
+ dangerouslySetInnerHTML: { __html: safeBr }
759
+ }),
760
+ tooltipSpan
761
+ );
623
762
  }
624
- return (0, import_react7.createElement)(
625
- as,
626
- {
627
- className: cn(className, editModeStyles),
628
- "data-content-key": contentKey,
629
- "data-editable": isEditMode ? "true" : void 0,
630
- onClick: isEditMode ? handleClick : void 0,
631
- title: isEditMode ? "Click to edit" : void 0
632
- },
633
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
634
- value,
635
- isEditMode && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "absolute -top-6 left-1/2 -translate-x-1/2 px-2 py-0.5 bg-blue-600 text-white text-[10px] rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-50", children: "Click to edit" })
636
- ] })
763
+ return (0, import_react8.createElement)(
764
+ "span",
765
+ { style: editModeWrapperStyle, ...hoverHandlers },
766
+ (0, import_react8.createElement)(
767
+ as,
768
+ {
769
+ className,
770
+ "data-content-key": contentKey,
771
+ "data-editable": isEditMode ? "true" : void 0,
772
+ onClick: isEditMode ? handleClick : void 0
773
+ },
774
+ value
775
+ ),
776
+ tooltipSpan
637
777
  );
638
778
  }
639
779
  function EditableHTML({
@@ -642,13 +782,20 @@ function EditableHTML({
642
782
  as = "div",
643
783
  className
644
784
  }) {
785
+ const [isHovered, setIsHovered] = (0, import_react8.useState)(false);
645
786
  const isEditMode = useEditModeMessaging();
646
787
  const sendEditRequest = useSendEditRequest();
647
788
  const { value, isLoading } = useSiteContentQuery(contentKey, {
648
789
  defaultValue
649
790
  });
650
791
  const section = contentKey.includes(".") ? contentKey.split(".")[0] : void 0;
651
- const handleClick = (0, import_react7.useCallback)(
792
+ (0, import_react8.useEffect)(() => {
793
+ if (isEditMode) {
794
+ registerContentKey(contentKey);
795
+ return () => unregisterContentKey(contentKey);
796
+ }
797
+ }, [isEditMode, contentKey]);
798
+ const handleClick = (0, import_react8.useCallback)(
652
799
  (e) => {
653
800
  if (isEditMode) {
654
801
  e.preventDefault();
@@ -659,26 +806,44 @@ function EditableHTML({
659
806
  [isEditMode, contentKey, value, section, sendEditRequest]
660
807
  );
661
808
  if (isLoading) {
662
- return (0, import_react7.createElement)(as, {
809
+ return (0, import_react8.createElement)(as, {
663
810
  className: cn("animate-pulse bg-muted rounded h-32", className),
664
811
  "aria-busy": true
665
812
  });
666
813
  }
667
- const editModeStyles = isEditMode ? cn(
668
- "cursor-pointer transition-all duration-200",
669
- "hover:ring-2 hover:ring-blue-500 hover:ring-offset-2",
670
- "hover:bg-blue-50/50 dark:hover:bg-blue-950/30",
671
- "relative group"
672
- ) : "";
814
+ const wrapperStyle = isEditMode ? {
815
+ position: "relative",
816
+ cursor: "pointer",
817
+ borderRadius: "2px",
818
+ outline: isHovered ? "2px solid rgb(59 130 246)" : "none",
819
+ outlineOffset: "2px",
820
+ backgroundColor: isHovered ? "rgba(59 130 246 / 0.08)" : void 0
821
+ } : {};
673
822
  const safeHtml = sanitizeHtml(value);
674
- return (0, import_react7.createElement)(as, {
675
- className: cn("prose prose-slate dark:prose-invert", className, editModeStyles),
676
- "data-content-key": contentKey,
677
- "data-editable": isEditMode ? "true" : void 0,
678
- onClick: isEditMode ? handleClick : void 0,
679
- title: isEditMode ? "Click to edit" : void 0,
680
- dangerouslySetInnerHTML: { __html: safeHtml }
681
- });
823
+ const hoverHandlers = isEditMode ? { onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false) } : {};
824
+ return (0, import_react8.createElement)(
825
+ "div",
826
+ { style: wrapperStyle, ...hoverHandlers },
827
+ (0, import_react8.createElement)(as, {
828
+ className: cn("prose prose-slate dark:prose-invert", className),
829
+ "data-content-key": contentKey,
830
+ "data-editable": isEditMode ? "true" : void 0,
831
+ onClick: isEditMode ? handleClick : void 0,
832
+ dangerouslySetInnerHTML: { __html: safeHtml }
833
+ }),
834
+ isEditMode && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
835
+ "span",
836
+ {
837
+ style: {
838
+ ...EDIT_MODE_TOOLTIP_STYLE,
839
+ top: "-24px",
840
+ opacity: isHovered ? 1 : 0
841
+ },
842
+ "aria-hidden": true,
843
+ children: "Click to edit"
844
+ }
845
+ )
846
+ );
682
847
  }
683
848
  function EditableImage({
684
849
  contentKey,
@@ -689,13 +854,20 @@ function EditableImage({
689
854
  height,
690
855
  priority = false
691
856
  }) {
857
+ const [isHovered, setIsHovered] = (0, import_react8.useState)(false);
692
858
  const isEditMode = useEditModeMessaging();
693
859
  const sendEditRequest = useSendEditRequest();
694
860
  const { value: src, isLoading } = useSiteContentQuery(contentKey, {
695
861
  defaultValue
696
862
  });
697
863
  const section = contentKey.includes(".") ? contentKey.split(".")[0] : void 0;
698
- const handleClick = (0, import_react7.useCallback)(
864
+ (0, import_react8.useEffect)(() => {
865
+ if (isEditMode) {
866
+ registerContentKey(contentKey);
867
+ return () => unregisterContentKey(contentKey);
868
+ }
869
+ }, [isEditMode, contentKey]);
870
+ const handleClick = (0, import_react8.useCallback)(
699
871
  (e) => {
700
872
  if (isEditMode) {
701
873
  e.preventDefault();
@@ -715,39 +887,55 @@ function EditableImage({
715
887
  }
716
888
  );
717
889
  }
718
- const editModeStyles = isEditMode ? cn(
719
- "cursor-pointer transition-all duration-200",
720
- "hover:ring-2 hover:ring-blue-500 hover:ring-offset-2",
721
- "hover:opacity-90",
722
- "relative group"
723
- ) : "";
724
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: cn("relative", isEditMode && "group"), children: [
890
+ const wrapperStyle = isEditMode ? {
891
+ position: "relative",
892
+ display: "inline-block",
893
+ cursor: "pointer",
894
+ borderRadius: "2px",
895
+ outline: isHovered ? "2px solid rgb(59 130 246)" : "none",
896
+ outlineOffset: "2px"
897
+ } : {};
898
+ const hoverHandlers = isEditMode ? { onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false) } : {};
899
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: wrapperStyle, ...hoverHandlers, children: [
725
900
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
726
901
  "img",
727
902
  {
728
903
  src,
729
904
  alt,
730
- className: cn(className, editModeStyles),
905
+ className,
906
+ style: isEditMode && isHovered ? { opacity: 0.95 } : void 0,
731
907
  width,
732
908
  height,
733
909
  loading: priority ? "eager" : "lazy",
734
910
  "data-content-key": contentKey,
735
911
  "data-editable": isEditMode ? "true" : void 0,
736
- onClick: isEditMode ? handleClick : void 0,
737
- title: isEditMode ? "Click to edit image" : void 0
912
+ onClick: isEditMode ? handleClick : void 0
738
913
  }
739
914
  ),
740
- isEditMode && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "absolute top-2 left-2 px-2 py-0.5 bg-blue-600 text-white text-[10px] rounded opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none", children: "Click to edit image" })
915
+ isEditMode && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
916
+ "span",
917
+ {
918
+ style: {
919
+ ...EDIT_MODE_TOOLTIP_STYLE,
920
+ top: "8px",
921
+ left: "8px",
922
+ transform: "none",
923
+ opacity: isHovered ? 1 : 0
924
+ },
925
+ "aria-hidden": true,
926
+ children: "Click to edit image"
927
+ }
928
+ )
741
929
  ] });
742
930
  }
743
931
 
744
932
  // src/hooks/useServices.ts
745
- var import_react8 = require("react");
933
+ var import_react9 = require("react");
746
934
  function useServices(options = {}) {
747
935
  const { client } = useFoxPixelContext();
748
- const [services, setServices] = (0, import_react8.useState)(null);
749
- const [isLoading, setIsLoading] = (0, import_react8.useState)(true);
750
- const [error, setError] = (0, import_react8.useState)(null);
936
+ const [services, setServices] = (0, import_react9.useState)(null);
937
+ const [isLoading, setIsLoading] = (0, import_react9.useState)(true);
938
+ const [error, setError] = (0, import_react9.useState)(null);
751
939
  const fetchServices = async () => {
752
940
  try {
753
941
  setIsLoading(true);
@@ -765,7 +953,7 @@ function useServices(options = {}) {
765
953
  setIsLoading(false);
766
954
  }
767
955
  };
768
- (0, import_react8.useEffect)(() => {
956
+ (0, import_react9.useEffect)(() => {
769
957
  fetchServices();
770
958
  }, [options.category, options.active]);
771
959
  return {
@@ -777,11 +965,11 @@ function useServices(options = {}) {
777
965
  }
778
966
 
779
967
  // src/hooks/useLeadCapture.ts
780
- var import_react9 = require("react");
968
+ var import_react10 = require("react");
781
969
  function useLeadCapture() {
782
970
  const { client } = useFoxPixelContext();
783
- const [isLoading, setIsLoading] = (0, import_react9.useState)(false);
784
- const [error, setError] = (0, import_react9.useState)(null);
971
+ const [isLoading, setIsLoading] = (0, import_react10.useState)(false);
972
+ const [error, setError] = (0, import_react10.useState)(null);
785
973
  const captureLead = async (data) => {
786
974
  try {
787
975
  setIsLoading(true);
@@ -804,11 +992,11 @@ function useLeadCapture() {
804
992
  }
805
993
 
806
994
  // src/hooks/useContactCapture.ts
807
- var import_react10 = require("react");
995
+ var import_react11 = require("react");
808
996
  function useContactCapture() {
809
997
  const { client } = useFoxPixelContext();
810
- const [isLoading, setIsLoading] = (0, import_react10.useState)(false);
811
- const [error, setError] = (0, import_react10.useState)(null);
998
+ const [isLoading, setIsLoading] = (0, import_react11.useState)(false);
999
+ const [error, setError] = (0, import_react11.useState)(null);
812
1000
  const captureContact = async (data) => {
813
1001
  try {
814
1002
  setIsLoading(true);
@@ -831,16 +1019,16 @@ function useContactCapture() {
831
1019
  }
832
1020
 
833
1021
  // src/hooks/useSiteContent.ts
834
- var import_react11 = require("react");
1022
+ var import_react12 = require("react");
835
1023
  function useSiteContent(contentKey, options = {}) {
836
1024
  const { defaultValue = "", fetchOnMount = true } = options;
837
1025
  const { client } = useFoxPixelContext();
838
1026
  const { user, hasPermission } = useAuth();
839
- const [data, setData] = (0, import_react11.useState)(null);
840
- const [isLoading, setIsLoading] = (0, import_react11.useState)(fetchOnMount);
841
- const [error, setError] = (0, import_react11.useState)(null);
1027
+ const [data, setData] = (0, import_react12.useState)(null);
1028
+ const [isLoading, setIsLoading] = (0, import_react12.useState)(fetchOnMount);
1029
+ const [error, setError] = (0, import_react12.useState)(null);
842
1030
  const canEdit = user !== null && hasPermission("site:content:update");
843
- const fetchContent = (0, import_react11.useCallback)(async () => {
1031
+ const fetchContent = (0, import_react12.useCallback)(async () => {
844
1032
  try {
845
1033
  setIsLoading(true);
846
1034
  setError(null);
@@ -858,7 +1046,7 @@ function useSiteContent(contentKey, options = {}) {
858
1046
  setIsLoading(false);
859
1047
  }
860
1048
  }, [client, contentKey]);
861
- const updateContent = (0, import_react11.useCallback)(async (newValue) => {
1049
+ const updateContent = (0, import_react12.useCallback)(async (newValue) => {
862
1050
  try {
863
1051
  setError(null);
864
1052
  const updated = await client.put(
@@ -871,7 +1059,7 @@ function useSiteContent(contentKey, options = {}) {
871
1059
  throw err;
872
1060
  }
873
1061
  }, [client, contentKey]);
874
- (0, import_react11.useEffect)(() => {
1062
+ (0, import_react12.useEffect)(() => {
875
1063
  if (fetchOnMount) {
876
1064
  fetchContent();
877
1065
  }
@@ -890,10 +1078,10 @@ function useSiteContent(contentKey, options = {}) {
890
1078
  function useSiteContents(contentKeys, options = {}) {
891
1079
  const { defaults = {} } = options;
892
1080
  const { client } = useFoxPixelContext();
893
- const [data, setData] = (0, import_react11.useState)({});
894
- const [isLoading, setIsLoading] = (0, import_react11.useState)(true);
895
- const [error, setError] = (0, import_react11.useState)(null);
896
- const fetchContents = (0, import_react11.useCallback)(async () => {
1081
+ const [data, setData] = (0, import_react12.useState)({});
1082
+ const [isLoading, setIsLoading] = (0, import_react12.useState)(true);
1083
+ const [error, setError] = (0, import_react12.useState)(null);
1084
+ const fetchContents = (0, import_react12.useCallback)(async () => {
897
1085
  if (contentKeys.length === 0) {
898
1086
  setData({});
899
1087
  setIsLoading(false);
@@ -913,10 +1101,10 @@ function useSiteContents(contentKeys, options = {}) {
913
1101
  setIsLoading(false);
914
1102
  }
915
1103
  }, [client, contentKeys.join(",")]);
916
- (0, import_react11.useEffect)(() => {
1104
+ (0, import_react12.useEffect)(() => {
917
1105
  fetchContents();
918
1106
  }, [fetchContents]);
919
- const getValue = (0, import_react11.useCallback)((key, defaultValue) => {
1107
+ const getValue = (0, import_react12.useCallback)((key, defaultValue) => {
920
1108
  const content = data[key];
921
1109
  if (content?.value) {
922
1110
  return content.value;
@@ -933,10 +1121,10 @@ function useSiteContents(contentKeys, options = {}) {
933
1121
  }
934
1122
  function useSiteContentSection(section) {
935
1123
  const { client } = useFoxPixelContext();
936
- const [contents, setContents] = (0, import_react11.useState)([]);
937
- const [isLoading, setIsLoading] = (0, import_react11.useState)(true);
938
- const [error, setError] = (0, import_react11.useState)(null);
939
- const fetchContents = (0, import_react11.useCallback)(async () => {
1124
+ const [contents, setContents] = (0, import_react12.useState)([]);
1125
+ const [isLoading, setIsLoading] = (0, import_react12.useState)(true);
1126
+ const [error, setError] = (0, import_react12.useState)(null);
1127
+ const fetchContents = (0, import_react12.useCallback)(async () => {
940
1128
  try {
941
1129
  setIsLoading(true);
942
1130
  setError(null);
@@ -950,7 +1138,7 @@ function useSiteContentSection(section) {
950
1138
  setIsLoading(false);
951
1139
  }
952
1140
  }, [client, section]);
953
- (0, import_react11.useEffect)(() => {
1141
+ (0, import_react12.useEffect)(() => {
954
1142
  fetchContents();
955
1143
  }, [fetchContents]);
956
1144
  return {
@@ -961,13 +1149,43 @@ function useSiteContentSection(section) {
961
1149
  };
962
1150
  }
963
1151
 
1152
+ // src/prefetchSiteContent.ts
1153
+ async function prefetchSiteContent(queryClient, options) {
1154
+ const { apiUrl, apiKey, tenantId, contentKeys } = options;
1155
+ const client = new FoxPixelHttpClient({ apiUrl, apiKey, tenantId });
1156
+ await Promise.all(
1157
+ contentKeys.map(async (contentKey) => {
1158
+ try {
1159
+ const content = await client.get(
1160
+ `/api/site/content/${encodeURIComponent(contentKey)}`
1161
+ );
1162
+ queryClient.setQueryData(
1163
+ [SITE_CONTENT_QUERY_KEY, contentKey],
1164
+ {
1165
+ value: content?.value ?? "",
1166
+ contentType: content?.contentType ?? "TEXT"
1167
+ }
1168
+ );
1169
+ } catch (err) {
1170
+ const status = err?.response?.status;
1171
+ if (status === 404) {
1172
+ queryClient.setQueryData([SITE_CONTENT_QUERY_KEY, contentKey], {
1173
+ value: "",
1174
+ contentType: "TEXT"
1175
+ });
1176
+ }
1177
+ }
1178
+ })
1179
+ );
1180
+ }
1181
+
964
1182
  // src/blog/hooks.ts
965
- var import_react12 = require("react");
1183
+ var import_react13 = require("react");
966
1184
  function useBlogPosts(options = {}) {
967
1185
  const { client } = useFoxPixelContext();
968
- const [data, setData] = (0, import_react12.useState)(null);
969
- const [isLoading, setIsLoading] = (0, import_react12.useState)(true);
970
- const [error, setError] = (0, import_react12.useState)(null);
1186
+ const [data, setData] = (0, import_react13.useState)(null);
1187
+ const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
1188
+ const [error, setError] = (0, import_react13.useState)(null);
971
1189
  const page = options.page ?? 0;
972
1190
  const limit = options.limit ?? 10;
973
1191
  const fetchPosts = async () => {
@@ -987,7 +1205,7 @@ function useBlogPosts(options = {}) {
987
1205
  setIsLoading(false);
988
1206
  }
989
1207
  };
990
- (0, import_react12.useEffect)(() => {
1208
+ (0, import_react13.useEffect)(() => {
991
1209
  fetchPosts();
992
1210
  }, [page, limit]);
993
1211
  return {
@@ -999,9 +1217,9 @@ function useBlogPosts(options = {}) {
999
1217
  }
1000
1218
  function useBlogPost(slug) {
1001
1219
  const { client } = useFoxPixelContext();
1002
- const [data, setData] = (0, import_react12.useState)(null);
1003
- const [isLoading, setIsLoading] = (0, import_react12.useState)(!!slug);
1004
- const [error, setError] = (0, import_react12.useState)(null);
1220
+ const [data, setData] = (0, import_react13.useState)(null);
1221
+ const [isLoading, setIsLoading] = (0, import_react13.useState)(!!slug);
1222
+ const [error, setError] = (0, import_react13.useState)(null);
1005
1223
  const fetchPost = async () => {
1006
1224
  if (!slug) {
1007
1225
  setData(null);
@@ -1020,7 +1238,7 @@ function useBlogPost(slug) {
1020
1238
  setIsLoading(false);
1021
1239
  }
1022
1240
  };
1023
- (0, import_react12.useEffect)(() => {
1241
+ (0, import_react13.useEffect)(() => {
1024
1242
  fetchPost();
1025
1243
  }, [slug]);
1026
1244
  return {
@@ -1032,9 +1250,9 @@ function useBlogPost(slug) {
1032
1250
  }
1033
1251
  function useBlogCategories() {
1034
1252
  const { client } = useFoxPixelContext();
1035
- const [data, setData] = (0, import_react12.useState)(null);
1036
- const [isLoading, setIsLoading] = (0, import_react12.useState)(true);
1037
- const [error, setError] = (0, import_react12.useState)(null);
1253
+ const [data, setData] = (0, import_react13.useState)(null);
1254
+ const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
1255
+ const [error, setError] = (0, import_react13.useState)(null);
1038
1256
  const fetchCategories = async () => {
1039
1257
  try {
1040
1258
  setIsLoading(true);
@@ -1048,7 +1266,7 @@ function useBlogCategories() {
1048
1266
  setIsLoading(false);
1049
1267
  }
1050
1268
  };
1051
- (0, import_react12.useEffect)(() => {
1269
+ (0, import_react13.useEffect)(() => {
1052
1270
  fetchCategories();
1053
1271
  }, []);
1054
1272
  return {
@@ -1060,9 +1278,9 @@ function useBlogCategories() {
1060
1278
  }
1061
1279
  function useBlogTags() {
1062
1280
  const { client } = useFoxPixelContext();
1063
- const [data, setData] = (0, import_react12.useState)(null);
1064
- const [isLoading, setIsLoading] = (0, import_react12.useState)(true);
1065
- const [error, setError] = (0, import_react12.useState)(null);
1281
+ const [data, setData] = (0, import_react13.useState)(null);
1282
+ const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
1283
+ const [error, setError] = (0, import_react13.useState)(null);
1066
1284
  const fetchTags = async () => {
1067
1285
  try {
1068
1286
  setIsLoading(true);
@@ -1076,7 +1294,7 @@ function useBlogTags() {
1076
1294
  setIsLoading(false);
1077
1295
  }
1078
1296
  };
1079
- (0, import_react12.useEffect)(() => {
1297
+ (0, import_react13.useEffect)(() => {
1080
1298
  fetchTags();
1081
1299
  }, []);
1082
1300
  return {
@@ -1088,9 +1306,9 @@ function useBlogTags() {
1088
1306
  }
1089
1307
  function useBlogComments(slug) {
1090
1308
  const { client } = useFoxPixelContext();
1091
- const [data, setData] = (0, import_react12.useState)(null);
1092
- const [isLoading, setIsLoading] = (0, import_react12.useState)(!!slug);
1093
- const [error, setError] = (0, import_react12.useState)(null);
1309
+ const [data, setData] = (0, import_react13.useState)(null);
1310
+ const [isLoading, setIsLoading] = (0, import_react13.useState)(!!slug);
1311
+ const [error, setError] = (0, import_react13.useState)(null);
1094
1312
  const fetchComments = async () => {
1095
1313
  if (!slug) {
1096
1314
  setData(null);
@@ -1111,7 +1329,7 @@ function useBlogComments(slug) {
1111
1329
  setIsLoading(false);
1112
1330
  }
1113
1331
  };
1114
- (0, import_react12.useEffect)(() => {
1332
+ (0, import_react13.useEffect)(() => {
1115
1333
  fetchComments();
1116
1334
  }, [slug]);
1117
1335
  return {
@@ -1123,8 +1341,8 @@ function useBlogComments(slug) {
1123
1341
  }
1124
1342
  function useBlogCommentSubmit(slug) {
1125
1343
  const { client } = useFoxPixelContext();
1126
- const [isSubmitting, setIsSubmitting] = (0, import_react12.useState)(false);
1127
- const [error, setError] = (0, import_react12.useState)(null);
1344
+ const [isSubmitting, setIsSubmitting] = (0, import_react13.useState)(false);
1345
+ const [error, setError] = (0, import_react13.useState)(null);
1128
1346
  const submit = async (payload) => {
1129
1347
  if (!slug) return null;
1130
1348
  try {
@@ -1152,9 +1370,9 @@ function useBlogCommentSubmit(slug) {
1152
1370
  }
1153
1371
  function useBlogFeaturedPosts(limit = 6) {
1154
1372
  const { client } = useFoxPixelContext();
1155
- const [data, setData] = (0, import_react12.useState)(null);
1156
- const [isLoading, setIsLoading] = (0, import_react12.useState)(true);
1157
- const [error, setError] = (0, import_react12.useState)(null);
1373
+ const [data, setData] = (0, import_react13.useState)(null);
1374
+ const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
1375
+ const [error, setError] = (0, import_react13.useState)(null);
1158
1376
  const fetchFeatured = async () => {
1159
1377
  try {
1160
1378
  setIsLoading(true);
@@ -1172,7 +1390,7 @@ function useBlogFeaturedPosts(limit = 6) {
1172
1390
  setIsLoading(false);
1173
1391
  }
1174
1392
  };
1175
- (0, import_react12.useEffect)(() => {
1393
+ (0, import_react13.useEffect)(() => {
1176
1394
  fetchFeatured();
1177
1395
  }, [limit]);
1178
1396
  return {
@@ -1184,9 +1402,9 @@ function useBlogFeaturedPosts(limit = 6) {
1184
1402
  }
1185
1403
  function useNewsletterSubscribe() {
1186
1404
  const { client } = useFoxPixelContext();
1187
- const [isSubmitting, setIsSubmitting] = (0, import_react12.useState)(false);
1188
- const [error, setError] = (0, import_react12.useState)(null);
1189
- const [success, setSuccess] = (0, import_react12.useState)(false);
1405
+ const [isSubmitting, setIsSubmitting] = (0, import_react13.useState)(false);
1406
+ const [error, setError] = (0, import_react13.useState)(null);
1407
+ const [success, setSuccess] = (0, import_react13.useState)(false);
1190
1408
  const subscribe = async (payload) => {
1191
1409
  try {
1192
1410
  setIsSubmitting(true);
@@ -1219,9 +1437,9 @@ function useNewsletterSubscribe() {
1219
1437
  }
1220
1438
  function useNewsletterUnsubscribe() {
1221
1439
  const { client } = useFoxPixelContext();
1222
- const [isSubmitting, setIsSubmitting] = (0, import_react12.useState)(false);
1223
- const [error, setError] = (0, import_react12.useState)(null);
1224
- const [success, setSuccess] = (0, import_react12.useState)(false);
1440
+ const [isSubmitting, setIsSubmitting] = (0, import_react13.useState)(false);
1441
+ const [error, setError] = (0, import_react13.useState)(null);
1442
+ const [success, setSuccess] = (0, import_react13.useState)(false);
1225
1443
  const unsubscribe = async (email) => {
1226
1444
  try {
1227
1445
  setIsSubmitting(true);
@@ -1266,15 +1484,15 @@ function useNewsletterUnsubscribe() {
1266
1484
  }
1267
1485
 
1268
1486
  // src/blog/admin-hooks.ts
1269
- var import_react13 = require("react");
1487
+ var import_react14 = require("react");
1270
1488
  function useAdminBlogPosts(options = {}) {
1271
1489
  const { client } = useFoxPixelContext();
1272
- const [data, setData] = (0, import_react13.useState)(null);
1273
- const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
1274
- const [error, setError] = (0, import_react13.useState)(null);
1490
+ const [data, setData] = (0, import_react14.useState)(null);
1491
+ const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
1492
+ const [error, setError] = (0, import_react14.useState)(null);
1275
1493
  const page = options.page ?? 0;
1276
1494
  const size = options.size ?? 20;
1277
- const fetchPosts = (0, import_react13.useCallback)(async () => {
1495
+ const fetchPosts = (0, import_react14.useCallback)(async () => {
1278
1496
  try {
1279
1497
  setIsLoading(true);
1280
1498
  setError(null);
@@ -1289,17 +1507,17 @@ function useAdminBlogPosts(options = {}) {
1289
1507
  setIsLoading(false);
1290
1508
  }
1291
1509
  }, [client, page, size]);
1292
- (0, import_react13.useEffect)(() => {
1510
+ (0, import_react14.useEffect)(() => {
1293
1511
  fetchPosts();
1294
1512
  }, [fetchPosts]);
1295
1513
  return { data, isLoading, error, refetch: fetchPosts };
1296
1514
  }
1297
1515
  function useAdminBlogPost(id) {
1298
1516
  const { client } = useFoxPixelContext();
1299
- const [data, setData] = (0, import_react13.useState)(null);
1300
- const [isLoading, setIsLoading] = (0, import_react13.useState)(!!id);
1301
- const [error, setError] = (0, import_react13.useState)(null);
1302
- const fetchPost = (0, import_react13.useCallback)(async () => {
1517
+ const [data, setData] = (0, import_react14.useState)(null);
1518
+ const [isLoading, setIsLoading] = (0, import_react14.useState)(!!id);
1519
+ const [error, setError] = (0, import_react14.useState)(null);
1520
+ const fetchPost = (0, import_react14.useCallback)(async () => {
1303
1521
  if (!id) {
1304
1522
  setData(null);
1305
1523
  setIsLoading(false);
@@ -1316,15 +1534,15 @@ function useAdminBlogPost(id) {
1316
1534
  setIsLoading(false);
1317
1535
  }
1318
1536
  }, [client, id]);
1319
- (0, import_react13.useEffect)(() => {
1537
+ (0, import_react14.useEffect)(() => {
1320
1538
  fetchPost();
1321
1539
  }, [fetchPost]);
1322
1540
  return { data, isLoading, error, refetch: fetchPost };
1323
1541
  }
1324
1542
  function useAdminBlogPostMutations() {
1325
1543
  const { client } = useFoxPixelContext();
1326
- const [isLoading, setIsLoading] = (0, import_react13.useState)(false);
1327
- const [error, setError] = (0, import_react13.useState)(null);
1544
+ const [isLoading, setIsLoading] = (0, import_react14.useState)(false);
1545
+ const [error, setError] = (0, import_react14.useState)(null);
1328
1546
  const create = async (payload) => {
1329
1547
  try {
1330
1548
  setIsLoading(true);
@@ -1368,10 +1586,10 @@ function useAdminBlogPostMutations() {
1368
1586
  }
1369
1587
  function useAdminBlogCategories() {
1370
1588
  const { client } = useFoxPixelContext();
1371
- const [data, setData] = (0, import_react13.useState)(null);
1372
- const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
1373
- const [error, setError] = (0, import_react13.useState)(null);
1374
- const fetchCategories = (0, import_react13.useCallback)(async () => {
1589
+ const [data, setData] = (0, import_react14.useState)(null);
1590
+ const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
1591
+ const [error, setError] = (0, import_react14.useState)(null);
1592
+ const fetchCategories = (0, import_react14.useCallback)(async () => {
1375
1593
  try {
1376
1594
  setIsLoading(true);
1377
1595
  setError(null);
@@ -1383,7 +1601,7 @@ function useAdminBlogCategories() {
1383
1601
  setIsLoading(false);
1384
1602
  }
1385
1603
  }, [client]);
1386
- (0, import_react13.useEffect)(() => {
1604
+ (0, import_react14.useEffect)(() => {
1387
1605
  fetchCategories();
1388
1606
  }, [fetchCategories]);
1389
1607
  const create = async (payload) => {
@@ -1420,10 +1638,10 @@ function useAdminBlogCategories() {
1420
1638
  }
1421
1639
  function useAdminBlogTags() {
1422
1640
  const { client } = useFoxPixelContext();
1423
- const [data, setData] = (0, import_react13.useState)(null);
1424
- const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
1425
- const [error, setError] = (0, import_react13.useState)(null);
1426
- const fetchTags = (0, import_react13.useCallback)(async () => {
1641
+ const [data, setData] = (0, import_react14.useState)(null);
1642
+ const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
1643
+ const [error, setError] = (0, import_react14.useState)(null);
1644
+ const fetchTags = (0, import_react14.useCallback)(async () => {
1427
1645
  try {
1428
1646
  setIsLoading(true);
1429
1647
  setError(null);
@@ -1435,7 +1653,7 @@ function useAdminBlogTags() {
1435
1653
  setIsLoading(false);
1436
1654
  }
1437
1655
  }, [client]);
1438
- (0, import_react13.useEffect)(() => {
1656
+ (0, import_react14.useEffect)(() => {
1439
1657
  fetchTags();
1440
1658
  }, [fetchTags]);
1441
1659
  const create = async (payload) => {
@@ -1472,11 +1690,11 @@ function useAdminBlogTags() {
1472
1690
  }
1473
1691
  function useAdminBlogComments(options = {}) {
1474
1692
  const { client } = useFoxPixelContext();
1475
- const [data, setData] = (0, import_react13.useState)(null);
1476
- const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
1477
- const [error, setError] = (0, import_react13.useState)(null);
1693
+ const [data, setData] = (0, import_react14.useState)(null);
1694
+ const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
1695
+ const [error, setError] = (0, import_react14.useState)(null);
1478
1696
  const { status, postId, page = 0, size = 20 } = options;
1479
- const fetchComments = (0, import_react13.useCallback)(async () => {
1697
+ const fetchComments = (0, import_react14.useCallback)(async () => {
1480
1698
  try {
1481
1699
  setIsLoading(true);
1482
1700
  setError(null);
@@ -1493,7 +1711,7 @@ function useAdminBlogComments(options = {}) {
1493
1711
  setIsLoading(false);
1494
1712
  }
1495
1713
  }, [client, status, postId, page, size]);
1496
- (0, import_react13.useEffect)(() => {
1714
+ (0, import_react14.useEffect)(() => {
1497
1715
  fetchComments();
1498
1716
  }, [fetchComments]);
1499
1717
  const updateStatus = async (id, newStatus) => {
@@ -1520,11 +1738,11 @@ function useAdminBlogComments(options = {}) {
1520
1738
  }
1521
1739
  function useAdminNewsletterSubscribers(options = {}) {
1522
1740
  const { client } = useFoxPixelContext();
1523
- const [data, setData] = (0, import_react13.useState)(null);
1524
- const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
1525
- const [error, setError] = (0, import_react13.useState)(null);
1741
+ const [data, setData] = (0, import_react14.useState)(null);
1742
+ const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
1743
+ const [error, setError] = (0, import_react14.useState)(null);
1526
1744
  const { status, page = 0, size = 20 } = options;
1527
- const fetchSubscribers = (0, import_react13.useCallback)(async () => {
1745
+ const fetchSubscribers = (0, import_react14.useCallback)(async () => {
1528
1746
  try {
1529
1747
  setIsLoading(true);
1530
1748
  setError(null);
@@ -1540,7 +1758,7 @@ function useAdminNewsletterSubscribers(options = {}) {
1540
1758
  setIsLoading(false);
1541
1759
  }
1542
1760
  }, [client, status, page, size]);
1543
- (0, import_react13.useEffect)(() => {
1761
+ (0, import_react14.useEffect)(() => {
1544
1762
  fetchSubscribers();
1545
1763
  }, [fetchSubscribers]);
1546
1764
  const remove = async (id) => {
@@ -1557,10 +1775,10 @@ function useAdminNewsletterSubscribers(options = {}) {
1557
1775
  }
1558
1776
  function useAdminNewsletterStats() {
1559
1777
  const { client } = useFoxPixelContext();
1560
- const [data, setData] = (0, import_react13.useState)(null);
1561
- const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
1562
- const [error, setError] = (0, import_react13.useState)(null);
1563
- const fetchStats = (0, import_react13.useCallback)(async () => {
1778
+ const [data, setData] = (0, import_react14.useState)(null);
1779
+ const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
1780
+ const [error, setError] = (0, import_react14.useState)(null);
1781
+ const fetchStats = (0, import_react14.useCallback)(async () => {
1564
1782
  try {
1565
1783
  setIsLoading(true);
1566
1784
  setError(null);
@@ -1572,17 +1790,17 @@ function useAdminNewsletterStats() {
1572
1790
  setIsLoading(false);
1573
1791
  }
1574
1792
  }, [client]);
1575
- (0, import_react13.useEffect)(() => {
1793
+ (0, import_react14.useEffect)(() => {
1576
1794
  fetchStats();
1577
1795
  }, [fetchStats]);
1578
1796
  return { data, isLoading, error, refetch: fetchStats };
1579
1797
  }
1580
1798
  function useAdminBlogSettings() {
1581
1799
  const { client } = useFoxPixelContext();
1582
- const [data, setData] = (0, import_react13.useState)(null);
1583
- const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
1584
- const [error, setError] = (0, import_react13.useState)(null);
1585
- const fetchSettings = (0, import_react13.useCallback)(async () => {
1800
+ const [data, setData] = (0, import_react14.useState)(null);
1801
+ const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
1802
+ const [error, setError] = (0, import_react14.useState)(null);
1803
+ const fetchSettings = (0, import_react14.useCallback)(async () => {
1586
1804
  try {
1587
1805
  setIsLoading(true);
1588
1806
  setError(null);
@@ -1594,7 +1812,7 @@ function useAdminBlogSettings() {
1594
1812
  setIsLoading(false);
1595
1813
  }
1596
1814
  }, [client]);
1597
- (0, import_react13.useEffect)(() => {
1815
+ (0, import_react14.useEffect)(() => {
1598
1816
  fetchSettings();
1599
1817
  }, [fetchSettings]);
1600
1818
  const update = async (settings) => {
@@ -1611,10 +1829,10 @@ function useAdminBlogSettings() {
1611
1829
  }
1612
1830
  function useAdminBlogAnalytics() {
1613
1831
  const { client } = useFoxPixelContext();
1614
- const [data, setData] = (0, import_react13.useState)(null);
1615
- const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
1616
- const [error, setError] = (0, import_react13.useState)(null);
1617
- const fetchAnalytics = (0, import_react13.useCallback)(async () => {
1832
+ const [data, setData] = (0, import_react14.useState)(null);
1833
+ const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
1834
+ const [error, setError] = (0, import_react14.useState)(null);
1835
+ const fetchAnalytics = (0, import_react14.useCallback)(async () => {
1618
1836
  try {
1619
1837
  setIsLoading(true);
1620
1838
  setError(null);
@@ -1626,7 +1844,7 @@ function useAdminBlogAnalytics() {
1626
1844
  setIsLoading(false);
1627
1845
  }
1628
1846
  }, [client]);
1629
- (0, import_react13.useEffect)(() => {
1847
+ (0, import_react14.useEffect)(() => {
1630
1848
  fetchAnalytics();
1631
1849
  }, [fetchAnalytics]);
1632
1850
  return { data, isLoading, error, refetch: fetchAnalytics };
@@ -1675,6 +1893,7 @@ function getBlogPostSchemaLd(post, options) {
1675
1893
  ProtectedRoute,
1676
1894
  SITE_CONTENT_QUERY_KEY,
1677
1895
  getBlogPostSchemaLd,
1896
+ prefetchSiteContent,
1678
1897
  useAdminBlogAnalytics,
1679
1898
  useAdminBlogCategories,
1680
1899
  useAdminBlogComments,