@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.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
- }), [client, config]);
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/hooks/useServices.ts
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, import_react6.useState)(null);
418
- const [isLoading, setIsLoading] = (0, import_react6.useState)(true);
419
- const [error, setError] = (0, import_react6.useState)(null);
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, import_react6.useEffect)(() => {
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 import_react7 = require("react");
928
+ var import_react10 = require("react");
450
929
  function useLeadCapture() {
451
930
  const { client } = useFoxPixelContext();
452
- const [isLoading, setIsLoading] = (0, import_react7.useState)(false);
453
- const [error, setError] = (0, import_react7.useState)(null);
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 import_react8 = require("react");
955
+ var import_react11 = require("react");
477
956
  function useContactCapture() {
478
957
  const { client } = useFoxPixelContext();
479
- const [isLoading, setIsLoading] = (0, import_react8.useState)(false);
480
- const [error, setError] = (0, import_react8.useState)(null);
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 import_react9 = require("react");
1143
+ var import_react13 = require("react");
504
1144
  function useBlogPosts(options = {}) {
505
1145
  const { client } = useFoxPixelContext();
506
- const [data, setData] = (0, import_react9.useState)(null);
507
- const [isLoading, setIsLoading] = (0, import_react9.useState)(true);
508
- const [error, setError] = (0, import_react9.useState)(null);
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, import_react9.useEffect)(() => {
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, import_react9.useState)(null);
541
- const [isLoading, setIsLoading] = (0, import_react9.useState)(!!slug);
542
- const [error, setError] = (0, import_react9.useState)(null);
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, import_react9.useEffect)(() => {
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, import_react9.useState)(null);
574
- const [isLoading, setIsLoading] = (0, import_react9.useState)(true);
575
- const [error, setError] = (0, import_react9.useState)(null);
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, import_react9.useEffect)(() => {
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, import_react9.useState)(null);
602
- const [isLoading, setIsLoading] = (0, import_react9.useState)(true);
603
- const [error, setError] = (0, import_react9.useState)(null);
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, import_react9.useEffect)(() => {
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, import_react9.useState)(null);
630
- const [isLoading, setIsLoading] = (0, import_react9.useState)(!!slug);
631
- const [error, setError] = (0, import_react9.useState)(null);
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, import_react9.useEffect)(() => {
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, import_react9.useState)(false);
665
- const [error, setError] = (0, import_react9.useState)(null);
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, import_react9.useState)(null);
694
- const [isLoading, setIsLoading] = (0, import_react9.useState)(true);
695
- const [error, setError] = (0, import_react9.useState)(null);
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, import_react9.useEffect)(() => {
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, import_react9.useState)(false);
726
- const [error, setError] = (0, import_react9.useState)(null);
727
- const [success, setSuccess] = (0, import_react9.useState)(false);
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, import_react9.useState)(false);
761
- const [error, setError] = (0, import_react9.useState)(null);
762
- const [success, setSuccess] = (0, import_react9.useState)(false);
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 import_react10 = require("react");
1447
+ var import_react14 = require("react");
790
1448
  function useAdminBlogPosts(options = {}) {
791
1449
  const { client } = useFoxPixelContext();
792
- const [data, setData] = (0, import_react10.useState)(null);
793
- const [isLoading, setIsLoading] = (0, import_react10.useState)(true);
794
- const [error, setError] = (0, import_react10.useState)(null);
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, import_react10.useCallback)(async () => {
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, import_react10.useEffect)(() => {
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, import_react10.useState)(null);
820
- const [isLoading, setIsLoading] = (0, import_react10.useState)(!!id);
821
- const [error, setError] = (0, import_react10.useState)(null);
822
- const fetchPost = (0, import_react10.useCallback)(async () => {
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, import_react10.useEffect)(() => {
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, import_react10.useState)(false);
847
- const [error, setError] = (0, import_react10.useState)(null);
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, import_react10.useState)(null);
892
- const [isLoading, setIsLoading] = (0, import_react10.useState)(true);
893
- const [error, setError] = (0, import_react10.useState)(null);
894
- const fetchCategories = (0, import_react10.useCallback)(async () => {
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, import_react10.useEffect)(() => {
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, import_react10.useState)(null);
944
- const [isLoading, setIsLoading] = (0, import_react10.useState)(true);
945
- const [error, setError] = (0, import_react10.useState)(null);
946
- const fetchTags = (0, import_react10.useCallback)(async () => {
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, import_react10.useEffect)(() => {
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, import_react10.useState)(null);
996
- const [isLoading, setIsLoading] = (0, import_react10.useState)(true);
997
- const [error, setError] = (0, import_react10.useState)(null);
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, import_react10.useCallback)(async () => {
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, import_react10.useEffect)(() => {
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, import_react10.useState)(null);
1044
- const [isLoading, setIsLoading] = (0, import_react10.useState)(true);
1045
- const [error, setError] = (0, import_react10.useState)(null);
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, import_react10.useCallback)(async () => {
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, import_react10.useEffect)(() => {
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, import_react10.useState)(null);
1081
- const [isLoading, setIsLoading] = (0, import_react10.useState)(true);
1082
- const [error, setError] = (0, import_react10.useState)(null);
1083
- const fetchStats = (0, import_react10.useCallback)(async () => {
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, import_react10.useEffect)(() => {
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, import_react10.useState)(null);
1103
- const [isLoading, setIsLoading] = (0, import_react10.useState)(true);
1104
- const [error, setError] = (0, import_react10.useState)(null);
1105
- const fetchSettings = (0, import_react10.useCallback)(async () => {
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, import_react10.useEffect)(() => {
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, import_react10.useState)(null);
1135
- const [isLoading, setIsLoading] = (0, import_react10.useState)(true);
1136
- const [error, setError] = (0, import_react10.useState)(null);
1137
- const fetchAnalytics = (0, import_react10.useCallback)(async () => {
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, import_react10.useEffect)(() => {
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