@foxpixel/react 0.1.0 → 0.2.0

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,15 +31,46 @@ 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,
42
+ getBlogPostSchemaLd: () => getBlogPostSchemaLd,
43
+ useAdminBlogAnalytics: () => useAdminBlogAnalytics,
44
+ useAdminBlogCategories: () => useAdminBlogCategories,
45
+ useAdminBlogComments: () => useAdminBlogComments,
46
+ useAdminBlogPost: () => useAdminBlogPost,
47
+ useAdminBlogPostMutations: () => useAdminBlogPostMutations,
48
+ useAdminBlogPosts: () => useAdminBlogPosts,
49
+ useAdminBlogSettings: () => useAdminBlogSettings,
50
+ useAdminBlogTags: () => useAdminBlogTags,
51
+ useAdminNewsletterStats: () => useAdminNewsletterStats,
52
+ useAdminNewsletterSubscribers: () => useAdminNewsletterSubscribers,
38
53
  useAuth: () => useAuth,
54
+ useBlogCategories: () => useBlogCategories,
55
+ useBlogCommentSubmit: () => useBlogCommentSubmit,
56
+ useBlogComments: () => useBlogComments,
57
+ useBlogFeaturedPosts: () => useBlogFeaturedPosts,
58
+ useBlogPost: () => useBlogPost,
59
+ useBlogPosts: () => useBlogPosts,
60
+ useBlogTags: () => useBlogTags,
39
61
  useContactCapture: () => useContactCapture,
62
+ useEditMode: () => useEditMode,
63
+ useEditModeMessaging: () => useEditModeMessaging,
40
64
  useFoxPixelContext: () => useFoxPixelContext,
41
65
  useLeadCapture: () => useLeadCapture,
66
+ useNewsletterSubscribe: () => useNewsletterSubscribe,
67
+ useNewsletterUnsubscribe: () => useNewsletterUnsubscribe,
68
+ useSendEditRequest: () => useSendEditRequest,
42
69
  useServices: () => useServices,
70
+ useSiteContent: () => useSiteContent,
71
+ useSiteContentQuery: () => useSiteContentQuery,
72
+ useSiteContentSection: () => useSiteContentSection,
73
+ useSiteContents: () => useSiteContents,
43
74
  withAuth: () => withAuth
44
75
  });
45
76
  module.exports = __toCommonJS(index_exports);
@@ -284,7 +315,8 @@ function AuthProvider({
284
315
  logout,
285
316
  register,
286
317
  updateProfile,
287
- refetch: fetchCurrentUser
318
+ refetch: fetchCurrentUser,
319
+ hasPermission: (permission) => user !== null && permission === "site:content:update"
288
320
  };
289
321
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AuthContext.Provider, { value, children });
290
322
  }
@@ -390,13 +422,332 @@ function withAuth(Component, options = {}) {
390
422
  };
391
423
  }
392
424
 
393
- // src/hooks/useServices.ts
425
+ // src/components/Editable.tsx
426
+ var import_react7 = require("react");
427
+
428
+ // src/hooks/useEditMode.ts
394
429
  var import_react6 = require("react");
430
+ var import_react_query = require("@tanstack/react-query");
431
+ var SITE_CONTENT_QUERY_KEY = "siteContent";
432
+ function useEditMode() {
433
+ const [isEditMode, setIsEditMode] = (0, import_react6.useState)(false);
434
+ (0, import_react6.useEffect)(() => {
435
+ if (typeof window === "undefined") return;
436
+ const params = new URLSearchParams(window.location.search);
437
+ setIsEditMode(params.get("edit-mode") === "true");
438
+ }, []);
439
+ return isEditMode;
440
+ }
441
+ function useEditModeMessaging() {
442
+ const queryClient = (0, import_react_query.useQueryClient)();
443
+ const isEditMode = useEditMode();
444
+ (0, import_react6.useEffect)(() => {
445
+ if (!isEditMode || typeof window === "undefined") return;
446
+ window.parent.postMessage({ type: "FOXPIXEL_READY" }, "*");
447
+ const handleMessage = (event) => {
448
+ 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
+ });
453
+ }
454
+ };
455
+ window.addEventListener("message", handleMessage);
456
+ return () => window.removeEventListener("message", handleMessage);
457
+ }, [isEditMode, queryClient]);
458
+ return isEditMode;
459
+ }
460
+ function useSendEditRequest() {
461
+ const isEditMode = useEditMode();
462
+ return (0, import_react6.useCallback)(
463
+ (contentKey, currentValue, contentType = "text", section, description) => {
464
+ if (!isEditMode) return;
465
+ if (typeof window !== "undefined" && window.parent !== window) {
466
+ window.parent.postMessage(
467
+ {
468
+ type: "FOXPIXEL_EDIT_CONTENT",
469
+ payload: {
470
+ contentKey,
471
+ currentValue,
472
+ contentType,
473
+ section,
474
+ description
475
+ }
476
+ },
477
+ "*"
478
+ );
479
+ }
480
+ },
481
+ [isEditMode]
482
+ );
483
+ }
484
+
485
+ // src/hooks/useSiteContentQuery.ts
486
+ var import_react_query2 = require("@tanstack/react-query");
487
+ function useSiteContentQuery(contentKey, options) {
488
+ 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 () => {
493
+ try {
494
+ const content = await client.get(
495
+ `/api/site/content/${encodeURIComponent(contentKey)}`
496
+ );
497
+ if (!content) return null;
498
+ return {
499
+ value: content.value ?? "",
500
+ contentType: content.contentType ?? "TEXT"
501
+ };
502
+ } catch (err) {
503
+ const status = err?.status;
504
+ if (status === 404) return null;
505
+ throw err;
506
+ }
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
+ };
516
+ }
517
+
518
+ // src/utils/sanitize.ts
519
+ var import_isomorphic_dompurify = __toESM(require("isomorphic-dompurify"));
520
+ var DEFAULT_ALLOWED_TAGS = [
521
+ "p",
522
+ "br",
523
+ "strong",
524
+ "em",
525
+ "u",
526
+ "s",
527
+ "a",
528
+ "ul",
529
+ "ol",
530
+ "li",
531
+ "h1",
532
+ "h2",
533
+ "h3",
534
+ "h4",
535
+ "h5",
536
+ "h6",
537
+ "blockquote",
538
+ "code",
539
+ "pre",
540
+ "span",
541
+ "div",
542
+ "img",
543
+ "table",
544
+ "thead",
545
+ "tbody",
546
+ "tr",
547
+ "th",
548
+ "td"
549
+ ];
550
+ var DEFAULT_ALLOWED_ATTR = ["href", "target", "rel", "src", "alt", "title", "class"];
551
+ function sanitizeHtml(html) {
552
+ 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"]
557
+ });
558
+ }
559
+
560
+ // src/utils/cn.ts
561
+ function cn(...args) {
562
+ return args.filter(Boolean).join(" ");
563
+ }
564
+
565
+ // src/components/Editable.tsx
566
+ var import_jsx_runtime6 = require("react/jsx-runtime");
567
+ function Editable({
568
+ contentKey,
569
+ defaultValue,
570
+ as = "span",
571
+ multiline = false,
572
+ className
573
+ }) {
574
+ const isEditMode = useEditModeMessaging();
575
+ const sendEditRequest = useSendEditRequest();
576
+ const { value, isLoading, contentType } = useSiteContentQuery(contentKey, {
577
+ defaultValue
578
+ });
579
+ const section = contentKey.includes(".") ? contentKey.split(".")[0] : void 0;
580
+ const handleClick = (0, import_react7.useCallback)(
581
+ (e) => {
582
+ if (isEditMode) {
583
+ e.preventDefault();
584
+ e.stopPropagation();
585
+ sendEditRequest(
586
+ contentKey,
587
+ value,
588
+ contentType?.toLowerCase() || "text",
589
+ section
590
+ );
591
+ }
592
+ },
593
+ [isEditMode, contentKey, value, contentType, section, sendEditRequest]
594
+ );
595
+ if (isLoading) {
596
+ return (0, import_react7.createElement)(as, {
597
+ className: cn(
598
+ "animate-pulse bg-muted rounded",
599
+ multiline ? "h-20" : "h-6",
600
+ "inline-block min-w-[100px]",
601
+ className
602
+ ),
603
+ "aria-busy": true,
604
+ "aria-label": "Loading content..."
605
+ });
606
+ }
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
+ ) : "";
613
+ if (multiline && value.includes("\n")) {
614
+ 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
+ });
623
+ }
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
+ ] })
637
+ );
638
+ }
639
+ function EditableHTML({
640
+ contentKey,
641
+ defaultValue,
642
+ as = "div",
643
+ className
644
+ }) {
645
+ const isEditMode = useEditModeMessaging();
646
+ const sendEditRequest = useSendEditRequest();
647
+ const { value, isLoading } = useSiteContentQuery(contentKey, {
648
+ defaultValue
649
+ });
650
+ const section = contentKey.includes(".") ? contentKey.split(".")[0] : void 0;
651
+ const handleClick = (0, import_react7.useCallback)(
652
+ (e) => {
653
+ if (isEditMode) {
654
+ e.preventDefault();
655
+ e.stopPropagation();
656
+ sendEditRequest(contentKey, value, "html", section);
657
+ }
658
+ },
659
+ [isEditMode, contentKey, value, section, sendEditRequest]
660
+ );
661
+ if (isLoading) {
662
+ return (0, import_react7.createElement)(as, {
663
+ className: cn("animate-pulse bg-muted rounded h-32", className),
664
+ "aria-busy": true
665
+ });
666
+ }
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
+ ) : "";
673
+ 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
+ });
682
+ }
683
+ function EditableImage({
684
+ contentKey,
685
+ defaultValue,
686
+ alt,
687
+ className,
688
+ width,
689
+ height,
690
+ priority = false
691
+ }) {
692
+ const isEditMode = useEditModeMessaging();
693
+ const sendEditRequest = useSendEditRequest();
694
+ const { value: src, isLoading } = useSiteContentQuery(contentKey, {
695
+ defaultValue
696
+ });
697
+ const section = contentKey.includes(".") ? contentKey.split(".")[0] : void 0;
698
+ const handleClick = (0, import_react7.useCallback)(
699
+ (e) => {
700
+ if (isEditMode) {
701
+ e.preventDefault();
702
+ e.stopPropagation();
703
+ sendEditRequest(contentKey, src, "image", section);
704
+ }
705
+ },
706
+ [isEditMode, contentKey, src, section, sendEditRequest]
707
+ );
708
+ if (isLoading) {
709
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
710
+ "div",
711
+ {
712
+ className: cn("animate-pulse bg-muted rounded", className),
713
+ style: { width, height },
714
+ "aria-busy": "true"
715
+ }
716
+ );
717
+ }
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: [
725
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
726
+ "img",
727
+ {
728
+ src,
729
+ alt,
730
+ className: cn(className, editModeStyles),
731
+ width,
732
+ height,
733
+ loading: priority ? "eager" : "lazy",
734
+ "data-content-key": contentKey,
735
+ "data-editable": isEditMode ? "true" : void 0,
736
+ onClick: isEditMode ? handleClick : void 0,
737
+ title: isEditMode ? "Click to edit image" : void 0
738
+ }
739
+ ),
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" })
741
+ ] });
742
+ }
743
+
744
+ // src/hooks/useServices.ts
745
+ var import_react8 = require("react");
395
746
  function useServices(options = {}) {
396
747
  const { client } = useFoxPixelContext();
397
- const [services, setServices] = (0, import_react6.useState)(null);
398
- const [isLoading, setIsLoading] = (0, import_react6.useState)(true);
399
- const [error, setError] = (0, import_react6.useState)(null);
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);
400
751
  const fetchServices = async () => {
401
752
  try {
402
753
  setIsLoading(true);
@@ -414,7 +765,7 @@ function useServices(options = {}) {
414
765
  setIsLoading(false);
415
766
  }
416
767
  };
417
- (0, import_react6.useEffect)(() => {
768
+ (0, import_react8.useEffect)(() => {
418
769
  fetchServices();
419
770
  }, [options.category, options.active]);
420
771
  return {
@@ -426,11 +777,11 @@ function useServices(options = {}) {
426
777
  }
427
778
 
428
779
  // src/hooks/useLeadCapture.ts
429
- var import_react7 = require("react");
780
+ var import_react9 = require("react");
430
781
  function useLeadCapture() {
431
782
  const { client } = useFoxPixelContext();
432
- const [isLoading, setIsLoading] = (0, import_react7.useState)(false);
433
- const [error, setError] = (0, import_react7.useState)(null);
783
+ const [isLoading, setIsLoading] = (0, import_react9.useState)(false);
784
+ const [error, setError] = (0, import_react9.useState)(null);
434
785
  const captureLead = async (data) => {
435
786
  try {
436
787
  setIsLoading(true);
@@ -453,11 +804,11 @@ function useLeadCapture() {
453
804
  }
454
805
 
455
806
  // src/hooks/useContactCapture.ts
456
- var import_react8 = require("react");
807
+ var import_react10 = require("react");
457
808
  function useContactCapture() {
458
809
  const { client } = useFoxPixelContext();
459
- const [isLoading, setIsLoading] = (0, import_react8.useState)(false);
460
- const [error, setError] = (0, import_react8.useState)(null);
810
+ const [isLoading, setIsLoading] = (0, import_react10.useState)(false);
811
+ const [error, setError] = (0, import_react10.useState)(null);
461
812
  const captureContact = async (data) => {
462
813
  try {
463
814
  setIsLoading(true);
@@ -478,18 +829,883 @@ function useContactCapture() {
478
829
  error
479
830
  };
480
831
  }
832
+
833
+ // src/hooks/useSiteContent.ts
834
+ var import_react11 = require("react");
835
+ function useSiteContent(contentKey, options = {}) {
836
+ const { defaultValue = "", fetchOnMount = true } = options;
837
+ const { client } = useFoxPixelContext();
838
+ 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);
842
+ const canEdit = user !== null && hasPermission("site:content:update");
843
+ const fetchContent = (0, import_react11.useCallback)(async () => {
844
+ try {
845
+ setIsLoading(true);
846
+ setError(null);
847
+ const content = await client.get(
848
+ `/api/site/content/${encodeURIComponent(contentKey)}`
849
+ );
850
+ setData(content);
851
+ } catch (err) {
852
+ if (err?.status === 404) {
853
+ setData(null);
854
+ } else {
855
+ setError(err);
856
+ }
857
+ } finally {
858
+ setIsLoading(false);
859
+ }
860
+ }, [client, contentKey]);
861
+ const updateContent = (0, import_react11.useCallback)(async (newValue) => {
862
+ try {
863
+ setError(null);
864
+ const updated = await client.put(
865
+ `/api/site/content/${encodeURIComponent(contentKey)}`,
866
+ { value: newValue }
867
+ );
868
+ setData(updated);
869
+ } catch (err) {
870
+ setError(err);
871
+ throw err;
872
+ }
873
+ }, [client, contentKey]);
874
+ (0, import_react11.useEffect)(() => {
875
+ if (fetchOnMount) {
876
+ fetchContent();
877
+ }
878
+ }, [contentKey, fetchOnMount]);
879
+ const value = data?.value ?? defaultValue;
880
+ return {
881
+ data,
882
+ value,
883
+ isLoading,
884
+ error,
885
+ canEdit,
886
+ update: updateContent,
887
+ refetch: fetchContent
888
+ };
889
+ }
890
+ function useSiteContents(contentKeys, options = {}) {
891
+ const { defaults = {} } = options;
892
+ 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 () => {
897
+ if (contentKeys.length === 0) {
898
+ setData({});
899
+ setIsLoading(false);
900
+ return;
901
+ }
902
+ try {
903
+ setIsLoading(true);
904
+ setError(null);
905
+ const contents = await client.post(
906
+ "/api/site/content/batch",
907
+ contentKeys
908
+ );
909
+ setData(contents);
910
+ } catch (err) {
911
+ setError(err);
912
+ } finally {
913
+ setIsLoading(false);
914
+ }
915
+ }, [client, contentKeys.join(",")]);
916
+ (0, import_react11.useEffect)(() => {
917
+ fetchContents();
918
+ }, [fetchContents]);
919
+ const getValue = (0, import_react11.useCallback)((key, defaultValue) => {
920
+ const content = data[key];
921
+ if (content?.value) {
922
+ return content.value;
923
+ }
924
+ return defaultValue ?? defaults[key] ?? "";
925
+ }, [data, defaults]);
926
+ return {
927
+ data,
928
+ getValue,
929
+ isLoading,
930
+ error,
931
+ refetch: fetchContents
932
+ };
933
+ }
934
+ function useSiteContentSection(section) {
935
+ 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 () => {
940
+ try {
941
+ setIsLoading(true);
942
+ setError(null);
943
+ const data = await client.get(
944
+ `/api/site/content/section/${encodeURIComponent(section)}`
945
+ );
946
+ setContents(data);
947
+ } catch (err) {
948
+ setError(err);
949
+ } finally {
950
+ setIsLoading(false);
951
+ }
952
+ }, [client, section]);
953
+ (0, import_react11.useEffect)(() => {
954
+ fetchContents();
955
+ }, [fetchContents]);
956
+ return {
957
+ contents,
958
+ isLoading,
959
+ error,
960
+ refetch: fetchContents
961
+ };
962
+ }
963
+
964
+ // src/blog/hooks.ts
965
+ var import_react12 = require("react");
966
+ function useBlogPosts(options = {}) {
967
+ 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);
971
+ const page = options.page ?? 0;
972
+ const limit = options.limit ?? 10;
973
+ const fetchPosts = async () => {
974
+ try {
975
+ setIsLoading(true);
976
+ setError(null);
977
+ const params = new URLSearchParams();
978
+ params.append("page", String(page));
979
+ params.append("size", String(limit));
980
+ const url = `/api/v1/blog/posts?${params.toString()}`;
981
+ const result = await client.get(url);
982
+ setData(result);
983
+ } catch (err) {
984
+ setError(err);
985
+ setData(null);
986
+ } finally {
987
+ setIsLoading(false);
988
+ }
989
+ };
990
+ (0, import_react12.useEffect)(() => {
991
+ fetchPosts();
992
+ }, [page, limit]);
993
+ return {
994
+ data,
995
+ isLoading,
996
+ error,
997
+ refetch: fetchPosts
998
+ };
999
+ }
1000
+ function useBlogPost(slug) {
1001
+ 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);
1005
+ const fetchPost = async () => {
1006
+ if (!slug) {
1007
+ setData(null);
1008
+ setIsLoading(false);
1009
+ return;
1010
+ }
1011
+ try {
1012
+ setIsLoading(true);
1013
+ setError(null);
1014
+ const result = await client.get(`/api/v1/blog/posts/${encodeURIComponent(slug)}`);
1015
+ setData(result);
1016
+ } catch (err) {
1017
+ setError(err);
1018
+ setData(null);
1019
+ } finally {
1020
+ setIsLoading(false);
1021
+ }
1022
+ };
1023
+ (0, import_react12.useEffect)(() => {
1024
+ fetchPost();
1025
+ }, [slug]);
1026
+ return {
1027
+ data,
1028
+ isLoading,
1029
+ error,
1030
+ refetch: fetchPost
1031
+ };
1032
+ }
1033
+ function useBlogCategories() {
1034
+ 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);
1038
+ const fetchCategories = async () => {
1039
+ try {
1040
+ setIsLoading(true);
1041
+ setError(null);
1042
+ const result = await client.get("/api/v1/blog/categories");
1043
+ setData(Array.isArray(result) ? result : []);
1044
+ } catch (err) {
1045
+ setError(err);
1046
+ setData(null);
1047
+ } finally {
1048
+ setIsLoading(false);
1049
+ }
1050
+ };
1051
+ (0, import_react12.useEffect)(() => {
1052
+ fetchCategories();
1053
+ }, []);
1054
+ return {
1055
+ data,
1056
+ isLoading,
1057
+ error,
1058
+ refetch: fetchCategories
1059
+ };
1060
+ }
1061
+ function useBlogTags() {
1062
+ 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);
1066
+ const fetchTags = async () => {
1067
+ try {
1068
+ setIsLoading(true);
1069
+ setError(null);
1070
+ const result = await client.get("/api/v1/blog/tags");
1071
+ setData(Array.isArray(result) ? result : []);
1072
+ } catch (err) {
1073
+ setError(err);
1074
+ setData(null);
1075
+ } finally {
1076
+ setIsLoading(false);
1077
+ }
1078
+ };
1079
+ (0, import_react12.useEffect)(() => {
1080
+ fetchTags();
1081
+ }, []);
1082
+ return {
1083
+ data,
1084
+ isLoading,
1085
+ error,
1086
+ refetch: fetchTags
1087
+ };
1088
+ }
1089
+ function useBlogComments(slug) {
1090
+ 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);
1094
+ const fetchComments = async () => {
1095
+ if (!slug) {
1096
+ setData(null);
1097
+ setIsLoading(false);
1098
+ return;
1099
+ }
1100
+ try {
1101
+ setIsLoading(true);
1102
+ setError(null);
1103
+ const result = await client.get(
1104
+ `/api/v1/blog/posts/${encodeURIComponent(slug)}/comments`
1105
+ );
1106
+ setData(Array.isArray(result) ? result : []);
1107
+ } catch (err) {
1108
+ setError(err);
1109
+ setData(null);
1110
+ } finally {
1111
+ setIsLoading(false);
1112
+ }
1113
+ };
1114
+ (0, import_react12.useEffect)(() => {
1115
+ fetchComments();
1116
+ }, [slug]);
1117
+ return {
1118
+ data,
1119
+ isLoading,
1120
+ error,
1121
+ refetch: fetchComments
1122
+ };
1123
+ }
1124
+ function useBlogCommentSubmit(slug) {
1125
+ const { client } = useFoxPixelContext();
1126
+ const [isSubmitting, setIsSubmitting] = (0, import_react12.useState)(false);
1127
+ const [error, setError] = (0, import_react12.useState)(null);
1128
+ const submit = async (payload) => {
1129
+ if (!slug) return null;
1130
+ try {
1131
+ setIsSubmitting(true);
1132
+ setError(null);
1133
+ const result = await client.post(
1134
+ `/api/v1/blog/posts/${encodeURIComponent(slug)}/comments`,
1135
+ payload
1136
+ );
1137
+ return result;
1138
+ } catch (err) {
1139
+ setError(err);
1140
+ return null;
1141
+ } finally {
1142
+ setIsSubmitting(false);
1143
+ }
1144
+ };
1145
+ const resetError = () => setError(null);
1146
+ return {
1147
+ submit,
1148
+ isSubmitting,
1149
+ error,
1150
+ resetError
1151
+ };
1152
+ }
1153
+ function useBlogFeaturedPosts(limit = 6) {
1154
+ 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);
1158
+ const fetchFeatured = async () => {
1159
+ try {
1160
+ setIsLoading(true);
1161
+ setError(null);
1162
+ const params = new URLSearchParams();
1163
+ params.append("page", "0");
1164
+ params.append("size", String(limit));
1165
+ const url = `/api/v1/blog/posts/featured?${params.toString()}`;
1166
+ const result = await client.get(url);
1167
+ setData(result);
1168
+ } catch (err) {
1169
+ setError(err);
1170
+ setData(null);
1171
+ } finally {
1172
+ setIsLoading(false);
1173
+ }
1174
+ };
1175
+ (0, import_react12.useEffect)(() => {
1176
+ fetchFeatured();
1177
+ }, [limit]);
1178
+ return {
1179
+ data,
1180
+ isLoading,
1181
+ error,
1182
+ refetch: fetchFeatured
1183
+ };
1184
+ }
1185
+ function useNewsletterSubscribe() {
1186
+ 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);
1190
+ const subscribe = async (payload) => {
1191
+ try {
1192
+ setIsSubmitting(true);
1193
+ setError(null);
1194
+ setSuccess(false);
1195
+ const result = await client.post(
1196
+ "/api/v1/blog/newsletter/subscribe",
1197
+ payload
1198
+ );
1199
+ setSuccess(true);
1200
+ return result;
1201
+ } catch (err) {
1202
+ setError(err);
1203
+ return null;
1204
+ } finally {
1205
+ setIsSubmitting(false);
1206
+ }
1207
+ };
1208
+ const reset = () => {
1209
+ setError(null);
1210
+ setSuccess(false);
1211
+ };
1212
+ return {
1213
+ subscribe,
1214
+ isSubmitting,
1215
+ error,
1216
+ success,
1217
+ reset
1218
+ };
1219
+ }
1220
+ function useNewsletterUnsubscribe() {
1221
+ 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);
1225
+ const unsubscribe = async (email) => {
1226
+ try {
1227
+ setIsSubmitting(true);
1228
+ setError(null);
1229
+ setSuccess(false);
1230
+ await client.post("/api/v1/blog/newsletter/unsubscribe", null, {
1231
+ params: { email }
1232
+ });
1233
+ setSuccess(true);
1234
+ return true;
1235
+ } catch (err) {
1236
+ setError(err);
1237
+ return false;
1238
+ } finally {
1239
+ setIsSubmitting(false);
1240
+ }
1241
+ };
1242
+ const unsubscribeByToken = async (token) => {
1243
+ try {
1244
+ setIsSubmitting(true);
1245
+ setError(null);
1246
+ setSuccess(false);
1247
+ await client.get("/api/v1/blog/newsletter/unsubscribe", {
1248
+ params: { token }
1249
+ });
1250
+ setSuccess(true);
1251
+ return true;
1252
+ } catch (err) {
1253
+ setError(err);
1254
+ return false;
1255
+ } finally {
1256
+ setIsSubmitting(false);
1257
+ }
1258
+ };
1259
+ return {
1260
+ unsubscribe,
1261
+ unsubscribeByToken,
1262
+ isSubmitting,
1263
+ error,
1264
+ success
1265
+ };
1266
+ }
1267
+
1268
+ // src/blog/admin-hooks.ts
1269
+ var import_react13 = require("react");
1270
+ function useAdminBlogPosts(options = {}) {
1271
+ 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);
1275
+ const page = options.page ?? 0;
1276
+ const size = options.size ?? 20;
1277
+ const fetchPosts = (0, import_react13.useCallback)(async () => {
1278
+ try {
1279
+ setIsLoading(true);
1280
+ setError(null);
1281
+ const params = new URLSearchParams();
1282
+ params.append("page", String(page));
1283
+ params.append("size", String(size));
1284
+ const result = await client.get(`/api/blog/posts?${params.toString()}`);
1285
+ setData(result);
1286
+ } catch (err) {
1287
+ setError(err);
1288
+ } finally {
1289
+ setIsLoading(false);
1290
+ }
1291
+ }, [client, page, size]);
1292
+ (0, import_react13.useEffect)(() => {
1293
+ fetchPosts();
1294
+ }, [fetchPosts]);
1295
+ return { data, isLoading, error, refetch: fetchPosts };
1296
+ }
1297
+ function useAdminBlogPost(id) {
1298
+ 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 () => {
1303
+ if (!id) {
1304
+ setData(null);
1305
+ setIsLoading(false);
1306
+ return;
1307
+ }
1308
+ try {
1309
+ setIsLoading(true);
1310
+ setError(null);
1311
+ const result = await client.get(`/api/blog/posts/${id}`);
1312
+ setData(result);
1313
+ } catch (err) {
1314
+ setError(err);
1315
+ } finally {
1316
+ setIsLoading(false);
1317
+ }
1318
+ }, [client, id]);
1319
+ (0, import_react13.useEffect)(() => {
1320
+ fetchPost();
1321
+ }, [fetchPost]);
1322
+ return { data, isLoading, error, refetch: fetchPost };
1323
+ }
1324
+ function useAdminBlogPostMutations() {
1325
+ const { client } = useFoxPixelContext();
1326
+ const [isLoading, setIsLoading] = (0, import_react13.useState)(false);
1327
+ const [error, setError] = (0, import_react13.useState)(null);
1328
+ const create = async (payload) => {
1329
+ try {
1330
+ setIsLoading(true);
1331
+ setError(null);
1332
+ const result = await client.post("/api/blog/posts", payload);
1333
+ return result;
1334
+ } catch (err) {
1335
+ setError(err);
1336
+ return null;
1337
+ } finally {
1338
+ setIsLoading(false);
1339
+ }
1340
+ };
1341
+ const update = async (id, payload) => {
1342
+ try {
1343
+ setIsLoading(true);
1344
+ setError(null);
1345
+ const result = await client.put(`/api/blog/posts/${id}`, payload);
1346
+ return result;
1347
+ } catch (err) {
1348
+ setError(err);
1349
+ return null;
1350
+ } finally {
1351
+ setIsLoading(false);
1352
+ }
1353
+ };
1354
+ const remove = async (id) => {
1355
+ try {
1356
+ setIsLoading(true);
1357
+ setError(null);
1358
+ await client.delete(`/api/blog/posts/${id}`);
1359
+ return true;
1360
+ } catch (err) {
1361
+ setError(err);
1362
+ return false;
1363
+ } finally {
1364
+ setIsLoading(false);
1365
+ }
1366
+ };
1367
+ return { create, update, remove, isLoading, error };
1368
+ }
1369
+ function useAdminBlogCategories() {
1370
+ 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 () => {
1375
+ try {
1376
+ setIsLoading(true);
1377
+ setError(null);
1378
+ const result = await client.get("/api/blog/categories");
1379
+ setData(Array.isArray(result) ? result : []);
1380
+ } catch (err) {
1381
+ setError(err);
1382
+ } finally {
1383
+ setIsLoading(false);
1384
+ }
1385
+ }, [client]);
1386
+ (0, import_react13.useEffect)(() => {
1387
+ fetchCategories();
1388
+ }, [fetchCategories]);
1389
+ const create = async (payload) => {
1390
+ try {
1391
+ const result = await client.post("/api/blog/categories", payload);
1392
+ await fetchCategories();
1393
+ return result;
1394
+ } catch (err) {
1395
+ setError(err);
1396
+ return null;
1397
+ }
1398
+ };
1399
+ const update = async (id, payload) => {
1400
+ try {
1401
+ const result = await client.put(`/api/blog/categories/${id}`, payload);
1402
+ await fetchCategories();
1403
+ return result;
1404
+ } catch (err) {
1405
+ setError(err);
1406
+ return null;
1407
+ }
1408
+ };
1409
+ const remove = async (id) => {
1410
+ try {
1411
+ await client.delete(`/api/blog/categories/${id}`);
1412
+ await fetchCategories();
1413
+ return true;
1414
+ } catch (err) {
1415
+ setError(err);
1416
+ return false;
1417
+ }
1418
+ };
1419
+ return { data, isLoading, error, refetch: fetchCategories, create, update, remove };
1420
+ }
1421
+ function useAdminBlogTags() {
1422
+ 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 () => {
1427
+ try {
1428
+ setIsLoading(true);
1429
+ setError(null);
1430
+ const result = await client.get("/api/blog/tags");
1431
+ setData(Array.isArray(result) ? result : []);
1432
+ } catch (err) {
1433
+ setError(err);
1434
+ } finally {
1435
+ setIsLoading(false);
1436
+ }
1437
+ }, [client]);
1438
+ (0, import_react13.useEffect)(() => {
1439
+ fetchTags();
1440
+ }, [fetchTags]);
1441
+ const create = async (payload) => {
1442
+ try {
1443
+ const result = await client.post("/api/blog/tags", payload);
1444
+ await fetchTags();
1445
+ return result;
1446
+ } catch (err) {
1447
+ setError(err);
1448
+ return null;
1449
+ }
1450
+ };
1451
+ const update = async (id, payload) => {
1452
+ try {
1453
+ const result = await client.put(`/api/blog/tags/${id}`, payload);
1454
+ await fetchTags();
1455
+ return result;
1456
+ } catch (err) {
1457
+ setError(err);
1458
+ return null;
1459
+ }
1460
+ };
1461
+ const remove = async (id) => {
1462
+ try {
1463
+ await client.delete(`/api/blog/tags/${id}`);
1464
+ await fetchTags();
1465
+ return true;
1466
+ } catch (err) {
1467
+ setError(err);
1468
+ return false;
1469
+ }
1470
+ };
1471
+ return { data, isLoading, error, refetch: fetchTags, create, update, remove };
1472
+ }
1473
+ function useAdminBlogComments(options = {}) {
1474
+ 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);
1478
+ const { status, postId, page = 0, size = 20 } = options;
1479
+ const fetchComments = (0, import_react13.useCallback)(async () => {
1480
+ try {
1481
+ setIsLoading(true);
1482
+ setError(null);
1483
+ const params = new URLSearchParams();
1484
+ params.append("page", String(page));
1485
+ params.append("size", String(size));
1486
+ if (status) params.append("status", status);
1487
+ if (postId) params.append("postId", postId);
1488
+ const result = await client.get(`/api/blog/comments?${params.toString()}`);
1489
+ setData(result);
1490
+ } catch (err) {
1491
+ setError(err);
1492
+ } finally {
1493
+ setIsLoading(false);
1494
+ }
1495
+ }, [client, status, postId, page, size]);
1496
+ (0, import_react13.useEffect)(() => {
1497
+ fetchComments();
1498
+ }, [fetchComments]);
1499
+ const updateStatus = async (id, newStatus) => {
1500
+ try {
1501
+ await client.put(`/api/blog/comments/${id}/status`, { status: newStatus });
1502
+ await fetchComments();
1503
+ return true;
1504
+ } catch (err) {
1505
+ setError(err);
1506
+ return false;
1507
+ }
1508
+ };
1509
+ const remove = async (id) => {
1510
+ try {
1511
+ await client.delete(`/api/blog/comments/${id}`);
1512
+ await fetchComments();
1513
+ return true;
1514
+ } catch (err) {
1515
+ setError(err);
1516
+ return false;
1517
+ }
1518
+ };
1519
+ return { data, isLoading, error, refetch: fetchComments, updateStatus, remove };
1520
+ }
1521
+ function useAdminNewsletterSubscribers(options = {}) {
1522
+ 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);
1526
+ const { status, page = 0, size = 20 } = options;
1527
+ const fetchSubscribers = (0, import_react13.useCallback)(async () => {
1528
+ try {
1529
+ setIsLoading(true);
1530
+ setError(null);
1531
+ const params = new URLSearchParams();
1532
+ params.append("page", String(page));
1533
+ params.append("size", String(size));
1534
+ if (status) params.append("status", status);
1535
+ const result = await client.get(`/api/blog/newsletter/subscribers?${params.toString()}`);
1536
+ setData(result);
1537
+ } catch (err) {
1538
+ setError(err);
1539
+ } finally {
1540
+ setIsLoading(false);
1541
+ }
1542
+ }, [client, status, page, size]);
1543
+ (0, import_react13.useEffect)(() => {
1544
+ fetchSubscribers();
1545
+ }, [fetchSubscribers]);
1546
+ const remove = async (id) => {
1547
+ try {
1548
+ await client.delete(`/api/blog/newsletter/subscribers/${id}`);
1549
+ await fetchSubscribers();
1550
+ return true;
1551
+ } catch (err) {
1552
+ setError(err);
1553
+ return false;
1554
+ }
1555
+ };
1556
+ return { data, isLoading, error, refetch: fetchSubscribers, remove };
1557
+ }
1558
+ function useAdminNewsletterStats() {
1559
+ 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 () => {
1564
+ try {
1565
+ setIsLoading(true);
1566
+ setError(null);
1567
+ const result = await client.get("/api/blog/newsletter/stats");
1568
+ setData(result);
1569
+ } catch (err) {
1570
+ setError(err);
1571
+ } finally {
1572
+ setIsLoading(false);
1573
+ }
1574
+ }, [client]);
1575
+ (0, import_react13.useEffect)(() => {
1576
+ fetchStats();
1577
+ }, [fetchStats]);
1578
+ return { data, isLoading, error, refetch: fetchStats };
1579
+ }
1580
+ function useAdminBlogSettings() {
1581
+ 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 () => {
1586
+ try {
1587
+ setIsLoading(true);
1588
+ setError(null);
1589
+ const result = await client.get("/api/blog/settings");
1590
+ setData(result);
1591
+ } catch (err) {
1592
+ setError(err);
1593
+ } finally {
1594
+ setIsLoading(false);
1595
+ }
1596
+ }, [client]);
1597
+ (0, import_react13.useEffect)(() => {
1598
+ fetchSettings();
1599
+ }, [fetchSettings]);
1600
+ const update = async (settings) => {
1601
+ try {
1602
+ const result = await client.put("/api/blog/settings", settings);
1603
+ setData(result);
1604
+ return result;
1605
+ } catch (err) {
1606
+ setError(err);
1607
+ return null;
1608
+ }
1609
+ };
1610
+ return { data, isLoading, error, refetch: fetchSettings, update };
1611
+ }
1612
+ function useAdminBlogAnalytics() {
1613
+ 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 () => {
1618
+ try {
1619
+ setIsLoading(true);
1620
+ setError(null);
1621
+ const result = await client.get("/api/blog/analytics/summary");
1622
+ setData(result);
1623
+ } catch (err) {
1624
+ setError(err);
1625
+ } finally {
1626
+ setIsLoading(false);
1627
+ }
1628
+ }, [client]);
1629
+ (0, import_react13.useEffect)(() => {
1630
+ fetchAnalytics();
1631
+ }, [fetchAnalytics]);
1632
+ return { data, isLoading, error, refetch: fetchAnalytics };
1633
+ }
1634
+
1635
+ // src/blog/utils.ts
1636
+ function getBlogPostSchemaLd(post, options) {
1637
+ const { siteUrl, publisherName, publisherLogoUrl } = options;
1638
+ const postUrl = `${siteUrl.replace(/\/$/, "")}/blog/${post.slug}`;
1639
+ const schema = {
1640
+ "@context": "https://schema.org",
1641
+ "@type": "BlogPosting",
1642
+ headline: post.title,
1643
+ description: post.metaDescription || post.excerpt || void 0,
1644
+ image: post.coverImageUrl || void 0,
1645
+ datePublished: post.publishedAt || void 0,
1646
+ dateModified: post.updatedAt,
1647
+ mainEntityOfPage: {
1648
+ "@type": "WebPage",
1649
+ "@id": postUrl
1650
+ }
1651
+ };
1652
+ if (publisherName || publisherLogoUrl) {
1653
+ schema.publisher = {
1654
+ "@type": "Organization",
1655
+ ...publisherName && { name: publisherName },
1656
+ ...publisherLogoUrl && {
1657
+ logo: {
1658
+ "@type": "ImageObject",
1659
+ url: publisherLogoUrl
1660
+ }
1661
+ }
1662
+ };
1663
+ }
1664
+ return schema;
1665
+ }
481
1666
  // Annotate the CommonJS export names for ESM import in node:
482
1667
  0 && (module.exports = {
483
1668
  AuthProvider,
1669
+ Editable,
1670
+ EditableHTML,
1671
+ EditableImage,
484
1672
  FoxPixelHttpClient,
485
1673
  FoxPixelProvider,
486
1674
  GuestOnlyRoute,
487
1675
  ProtectedRoute,
1676
+ SITE_CONTENT_QUERY_KEY,
1677
+ getBlogPostSchemaLd,
1678
+ useAdminBlogAnalytics,
1679
+ useAdminBlogCategories,
1680
+ useAdminBlogComments,
1681
+ useAdminBlogPost,
1682
+ useAdminBlogPostMutations,
1683
+ useAdminBlogPosts,
1684
+ useAdminBlogSettings,
1685
+ useAdminBlogTags,
1686
+ useAdminNewsletterStats,
1687
+ useAdminNewsletterSubscribers,
488
1688
  useAuth,
1689
+ useBlogCategories,
1690
+ useBlogCommentSubmit,
1691
+ useBlogComments,
1692
+ useBlogFeaturedPosts,
1693
+ useBlogPost,
1694
+ useBlogPosts,
1695
+ useBlogTags,
489
1696
  useContactCapture,
1697
+ useEditMode,
1698
+ useEditModeMessaging,
490
1699
  useFoxPixelContext,
491
1700
  useLeadCapture,
1701
+ useNewsletterSubscribe,
1702
+ useNewsletterUnsubscribe,
1703
+ useSendEditRequest,
492
1704
  useServices,
1705
+ useSiteContent,
1706
+ useSiteContentQuery,
1707
+ useSiteContentSection,
1708
+ useSiteContents,
493
1709
  withAuth
494
1710
  });
495
1711
  //# sourceMappingURL=index.js.map