@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.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { ReactNode, ComponentType } from 'react';
2
+ import React, { ReactNode, ComponentType } from 'react';
3
3
  import { AxiosInstance, AxiosRequestConfig } from 'axios';
4
4
 
5
5
  /**
@@ -188,6 +188,8 @@ interface AuthContextValue {
188
188
  phone?: string;
189
189
  }) => Promise<void>;
190
190
  refetch: () => Promise<void>;
191
+ /** Stub: returns true when user is logged in and permission is site:content:update. */
192
+ hasPermission: (permission: string) => boolean;
191
193
  }
192
194
  interface AuthProviderProps {
193
195
  children: ReactNode;
@@ -261,6 +263,54 @@ interface WithAuthOptions {
261
263
  }
262
264
  declare function withAuth<P extends object>(Component: ComponentType<P>, options?: WithAuthOptions): (props: P) => string | number | true | Iterable<ReactNode> | react_jsx_runtime.JSX.Element | null;
263
265
 
266
+ interface EditableProps {
267
+ contentKey: string;
268
+ defaultValue: string;
269
+ as?: keyof JSX.IntrinsicElements;
270
+ multiline?: boolean;
271
+ className?: string;
272
+ }
273
+ declare function Editable({ contentKey, defaultValue, as, multiline, className, }: EditableProps): React.ReactElement<{
274
+ className: string;
275
+ 'aria-busy': boolean;
276
+ 'aria-label': string;
277
+ }, string | React.JSXElementConstructor<any>> | React.DOMElement<{
278
+ className: string;
279
+ 'data-content-key': string;
280
+ 'data-editable': string | undefined;
281
+ onClick: ((e: React.MouseEvent) => void) | undefined;
282
+ title: string | undefined;
283
+ }, Element>;
284
+ /**
285
+ * Renders rich HTML content from the CMS. Content is sanitized before rendering.
286
+ */
287
+ declare function EditableHTML({ contentKey, defaultValue, as, className, }: Omit<EditableProps, 'multiline'>): React.ReactElement<{
288
+ className: string;
289
+ 'aria-busy': boolean;
290
+ }, string | React.JSXElementConstructor<any>> | React.DOMElement<{
291
+ className: string;
292
+ 'data-content-key': string;
293
+ 'data-editable': string | undefined;
294
+ onClick: ((e: React.MouseEvent) => void) | undefined;
295
+ title: string | undefined;
296
+ dangerouslySetInnerHTML: {
297
+ __html: string;
298
+ };
299
+ }, Element>;
300
+ interface EditableImageProps {
301
+ contentKey: string;
302
+ defaultValue: string;
303
+ alt: string;
304
+ className?: string;
305
+ width?: number;
306
+ height?: number;
307
+ priority?: boolean;
308
+ }
309
+ /**
310
+ * Renders an image from a CMS-managed URL. Uses native img (no Next.js Image in SDK).
311
+ */
312
+ declare function EditableImage({ contentKey, defaultValue, alt, className, width, height, priority, }: EditableImageProps): react_jsx_runtime.JSX.Element;
313
+
264
314
  /**
265
315
  * Hook to fetch and manage services (Projects module)
266
316
  */
@@ -405,4 +455,693 @@ interface UseContactCaptureReturn {
405
455
  */
406
456
  declare function useContactCapture(): UseContactCaptureReturn;
407
457
 
408
- export { type ApiError$1 as ApiError, AuthProvider, type CreateLeadRequest, type EndUser, type EndUserLoginRequest, type FoxPixelConfig, FoxPixelHttpClient, FoxPixelProvider, GuestOnlyRoute, type Lead, ProtectedRoute, type Service, type ServiceCatalogResponse, useAuth, useContactCapture, useFoxPixelContext, useLeadCapture, useServices, withAuth };
458
+ /**
459
+ * Hook for managing editable site content (CMS Visual)
460
+ *
461
+ * Allows non-developers to edit text content directly on the site.
462
+ */
463
+
464
+ interface SiteContent {
465
+ id: string;
466
+ contentKey: string;
467
+ value: string | null;
468
+ contentType: string;
469
+ defaultValue: string | null;
470
+ description: string | null;
471
+ section: string | null;
472
+ sortOrder: number;
473
+ isActive: boolean;
474
+ updatedAt: string | null;
475
+ }
476
+ interface UseSiteContentOptions {
477
+ /**
478
+ * Default value to use if content is not found
479
+ */
480
+ defaultValue?: string;
481
+ /**
482
+ * Whether to fetch on mount (default: true)
483
+ */
484
+ fetchOnMount?: boolean;
485
+ }
486
+ interface UseSiteContentReturn {
487
+ /**
488
+ * The content object (null if not found or loading)
489
+ */
490
+ data: SiteContent | null;
491
+ /**
492
+ * The content value (or defaultValue if not found)
493
+ */
494
+ value: string;
495
+ /**
496
+ * Whether content is being loaded
497
+ */
498
+ isLoading: boolean;
499
+ /**
500
+ * Error if request failed
501
+ */
502
+ error: ApiError$1 | null;
503
+ /**
504
+ * Whether the current user can edit this content
505
+ */
506
+ canEdit: boolean;
507
+ /**
508
+ * Update the content value
509
+ */
510
+ update: (newValue: string) => Promise<void>;
511
+ /**
512
+ * Refetch the content
513
+ */
514
+ refetch: () => Promise<void>;
515
+ }
516
+ /**
517
+ * Hook for managing a single site content key.
518
+ *
519
+ * @example
520
+ * ```tsx
521
+ * function HeroSection() {
522
+ * const { value, canEdit, update } = useSiteContent('hero.title', {
523
+ * defaultValue: 'Welcome to our site'
524
+ * });
525
+ *
526
+ * const [isEditing, setIsEditing] = useState(false);
527
+ * const [editValue, setEditValue] = useState(value);
528
+ *
529
+ * const handleSave = async () => {
530
+ * await update(editValue);
531
+ * setIsEditing(false);
532
+ * };
533
+ *
534
+ * if (canEdit && isEditing) {
535
+ * return (
536
+ * <input
537
+ * value={editValue}
538
+ * onChange={(e) => setEditValue(e.target.value)}
539
+ * onBlur={handleSave}
540
+ * />
541
+ * );
542
+ * }
543
+ *
544
+ * return (
545
+ * <h1 onClick={() => canEdit && setIsEditing(true)}>
546
+ * {value}
547
+ * </h1>
548
+ * );
549
+ * }
550
+ * ```
551
+ */
552
+ declare function useSiteContent(contentKey: string, options?: UseSiteContentOptions): UseSiteContentReturn;
553
+ interface UseSiteContentsOptions {
554
+ /**
555
+ * Default values map
556
+ */
557
+ defaults?: Record<string, string>;
558
+ }
559
+ interface UseSiteContentsReturn {
560
+ /**
561
+ * Map of content key to content object
562
+ */
563
+ data: Record<string, SiteContent>;
564
+ /**
565
+ * Get value by key (with default fallback)
566
+ */
567
+ getValue: (key: string, defaultValue?: string) => string;
568
+ /**
569
+ * Whether contents are being loaded
570
+ */
571
+ isLoading: boolean;
572
+ /**
573
+ * Error if request failed
574
+ */
575
+ error: ApiError$1 | null;
576
+ /**
577
+ * Refetch all contents
578
+ */
579
+ refetch: () => Promise<void>;
580
+ }
581
+ /**
582
+ * Hook for fetching multiple site content keys at once.
583
+ * More efficient than calling useSiteContent multiple times.
584
+ *
585
+ * @example
586
+ * ```tsx
587
+ * function Footer() {
588
+ * const { getValue, isLoading } = useSiteContents(
589
+ * ['footer.copyright', 'footer.address', 'footer.phone'],
590
+ * { defaults: { 'footer.copyright': '© 2024' } }
591
+ * );
592
+ *
593
+ * if (isLoading) return null;
594
+ *
595
+ * return (
596
+ * <footer>
597
+ * <p>{getValue('footer.copyright')}</p>
598
+ * <p>{getValue('footer.address')}</p>
599
+ * <p>{getValue('footer.phone')}</p>
600
+ * </footer>
601
+ * );
602
+ * }
603
+ * ```
604
+ */
605
+ declare function useSiteContents(contentKeys: string[], options?: UseSiteContentsOptions): UseSiteContentsReturn;
606
+ interface UseSiteContentSectionReturn {
607
+ /**
608
+ * List of contents in the section
609
+ */
610
+ contents: SiteContent[];
611
+ /**
612
+ * Whether contents are being loaded
613
+ */
614
+ isLoading: boolean;
615
+ /**
616
+ * Error if request failed
617
+ */
618
+ error: ApiError$1 | null;
619
+ /**
620
+ * Refetch section contents
621
+ */
622
+ refetch: () => Promise<void>;
623
+ }
624
+ /**
625
+ * Hook for fetching all content in a section.
626
+ *
627
+ * @example
628
+ * ```tsx
629
+ * function HeroSection() {
630
+ * const { contents, isLoading } = useSiteContentSection('hero');
631
+ *
632
+ * if (isLoading) return <div>Loading...</div>;
633
+ *
634
+ * return (
635
+ * <div>
636
+ * {contents.map(content => (
637
+ * <div key={content.contentKey}>{content.value}</div>
638
+ * ))}
639
+ * </div>
640
+ * );
641
+ * }
642
+ * ```
643
+ */
644
+ declare function useSiteContentSection(section: string): UseSiteContentSectionReturn;
645
+
646
+ /**
647
+ * Edit-mode hooks for CMS Visual (iframe communication with Tenant Admin).
648
+ * When edit-mode=true is in the URL, the site enters edit mode and communicates
649
+ * with the parent iframe via postMessage.
650
+ */
651
+ /** Query key used for site content; must match useSiteContentQuery. */
652
+ declare const SITE_CONTENT_QUERY_KEY: "siteContent";
653
+ /**
654
+ * Returns true when the page is in edit mode (URL has edit-mode=true).
655
+ * Used to show edit affordances and enable postMessage communication.
656
+ */
657
+ declare function useEditMode(): boolean;
658
+ /**
659
+ * Sets up edit-mode messaging: notifies parent when ready and listens for
660
+ * FOXPIXEL_CONTENT_UPDATED to invalidate the corresponding site content query.
661
+ * App must be wrapped in QueryClientProvider when using edit mode.
662
+ */
663
+ declare function useEditModeMessaging(): boolean;
664
+ /**
665
+ * Returns a callback to send an edit request to the parent iframe.
666
+ * Only has effect when in edit mode.
667
+ */
668
+ declare function useSendEditRequest(): (contentKey: string, currentValue: string, contentType?: string, section?: string, description?: string) => void;
669
+
670
+ /**
671
+ * React Query-based hook for site content used by Editable components.
672
+ * Uses queryKey ['siteContent', key] so that useEditModeMessaging can invalidate
673
+ * on FOXPIXEL_CONTENT_UPDATED. Requires QueryClientProvider.
674
+ */
675
+ interface UseSiteContentQueryOptions {
676
+ defaultValue: string;
677
+ }
678
+ interface UseSiteContentQueryReturn {
679
+ value: string;
680
+ isLoading: boolean;
681
+ contentType: string;
682
+ }
683
+ /**
684
+ * Fetches a single site content by key using React Query. Used by Editable, EditableHTML, EditableImage.
685
+ * Invalidation is triggered by useEditModeMessaging when the parent sends FOXPIXEL_CONTENT_UPDATED.
686
+ */
687
+ declare function useSiteContentQuery(contentKey: string, options: UseSiteContentQueryOptions): UseSiteContentQueryReturn;
688
+
689
+ /**
690
+ * Blog module types for FoxPixel SDK
691
+ * Aligned with backend BlogPostResponse, BlogCategoryResponse, BlogTagResponse
692
+ */
693
+ interface BlogAuthor {
694
+ id: string;
695
+ name: string;
696
+ email?: string;
697
+ }
698
+ interface BlogCategory {
699
+ id: string;
700
+ name: string;
701
+ slug: string;
702
+ description?: string;
703
+ color?: string;
704
+ sortOrder?: number;
705
+ }
706
+ interface BlogTag {
707
+ id: string;
708
+ name: string;
709
+ slug: string;
710
+ }
711
+ type BlogPostStatus = 'DRAFT' | 'SCHEDULED' | 'PUBLISHED' | 'ARCHIVED';
712
+ interface BlogPost {
713
+ id: string;
714
+ title: string;
715
+ slug: string;
716
+ excerpt: string | null;
717
+ content: string;
718
+ coverImageUrl: string | null;
719
+ metaTitle: string | null;
720
+ metaDescription: string | null;
721
+ canonicalUrl: string | null;
722
+ ogTitle: string | null;
723
+ ogDescription: string | null;
724
+ ogImageUrl: string | null;
725
+ status: BlogPostStatus;
726
+ publishedAt: string | null;
727
+ scheduledAt: string | null;
728
+ readingTimeMinutes: number | null;
729
+ isFeatured: boolean;
730
+ allowComments: boolean;
731
+ authorId: string | null;
732
+ author?: BlogAuthor | null;
733
+ version: number;
734
+ isAiGenerated: boolean;
735
+ categories: BlogCategory[];
736
+ tags: BlogTag[];
737
+ createdAt: string;
738
+ updatedAt: string;
739
+ }
740
+ /** Spring Data Page response shape */
741
+ interface BlogPostPage {
742
+ content: BlogPost[];
743
+ totalElements: number;
744
+ totalPages: number;
745
+ size: number;
746
+ number: number;
747
+ first: boolean;
748
+ last: boolean;
749
+ numberOfElements: number;
750
+ empty: boolean;
751
+ }
752
+ /** Blog comment (approved, for display) */
753
+ interface BlogComment {
754
+ id: string;
755
+ postId: string;
756
+ postTitle?: string | null;
757
+ postSlug?: string | null;
758
+ parentId?: string | null;
759
+ authorName?: string | null;
760
+ content: string;
761
+ status: string;
762
+ createdAt: string;
763
+ replies?: BlogComment[];
764
+ }
765
+ /** Payload to create a comment (guest) */
766
+ interface CreateBlogCommentPayload {
767
+ guestName?: string;
768
+ guestEmail?: string;
769
+ content: string;
770
+ parentId?: string;
771
+ }
772
+ type NewsletterFrequency = 'instant' | 'daily' | 'weekly' | 'monthly';
773
+ type NewsletterStatus = 'ACTIVE' | 'UNSUBSCRIBED' | 'BOUNCED';
774
+ interface NewsletterSubscriber {
775
+ id: string;
776
+ email: string;
777
+ name?: string | null;
778
+ frequency: NewsletterFrequency;
779
+ categories?: string[] | null;
780
+ status: NewsletterStatus;
781
+ source?: string | null;
782
+ confirmedAt?: string | null;
783
+ unsubscribedAt?: string | null;
784
+ createdAt: string;
785
+ }
786
+ interface SubscribeNewsletterPayload {
787
+ email: string;
788
+ name?: string;
789
+ frequency?: NewsletterFrequency;
790
+ categories?: string[];
791
+ source?: string;
792
+ }
793
+ interface NewsletterSubscriberPage {
794
+ content: NewsletterSubscriber[];
795
+ totalElements: number;
796
+ totalPages: number;
797
+ size: number;
798
+ number: number;
799
+ first: boolean;
800
+ last: boolean;
801
+ numberOfElements: number;
802
+ empty: boolean;
803
+ }
804
+ interface NewsletterStats {
805
+ activeSubscribers: number;
806
+ }
807
+ interface BlogSettings {
808
+ businessContext?: string | null;
809
+ defaultLanguage?: string | null;
810
+ automationEnabled?: boolean | null;
811
+ autoPublishAsDraft?: boolean | null;
812
+ siteUrl?: string | null;
813
+ organizationName?: string | null;
814
+ blogName?: string | null;
815
+ }
816
+ interface BlogAnalyticsSummary {
817
+ totalViews: number;
818
+ totalUniqueVisitors: number;
819
+ totalPosts: number;
820
+ publishedPosts: number;
821
+ draftPosts: number;
822
+ scheduledPosts: number;
823
+ topPosts?: Array<{
824
+ postId: string;
825
+ postTitle: string;
826
+ postSlug: string;
827
+ views: number;
828
+ }>;
829
+ }
830
+ interface PostAnalyticsRow {
831
+ postId: string;
832
+ postTitle: string;
833
+ postSlug: string;
834
+ date: string;
835
+ views: number;
836
+ uniqueVisitors: number;
837
+ }
838
+
839
+ /**
840
+ * Hooks for Blog module (public API - SDK / headless)
841
+ */
842
+
843
+ interface UseBlogPostsOptions {
844
+ page?: number;
845
+ limit?: number;
846
+ }
847
+ interface UseBlogPostsReturn {
848
+ data: BlogPostPage | null;
849
+ isLoading: boolean;
850
+ error: ApiError$1 | null;
851
+ refetch: () => Promise<void>;
852
+ }
853
+ /**
854
+ * Fetch published blog posts (paginated)
855
+ *
856
+ * @example
857
+ * ```tsx
858
+ * const { data, isLoading, error } = useBlogPosts({ page: 0, limit: 10 });
859
+ * data?.content.map(post => <PostCard key={post.id} post={post} />)
860
+ * ```
861
+ */
862
+ declare function useBlogPosts(options?: UseBlogPostsOptions): UseBlogPostsReturn;
863
+ interface UseBlogPostReturn {
864
+ data: BlogPost | null;
865
+ isLoading: boolean;
866
+ error: ApiError$1 | null;
867
+ refetch: () => Promise<void>;
868
+ }
869
+ /**
870
+ * Fetch a single published blog post by slug
871
+ *
872
+ * @example
873
+ * ```tsx
874
+ * const { data: post, isLoading } = useBlogPost('my-post-slug');
875
+ * ```
876
+ */
877
+ declare function useBlogPost(slug: string | undefined | null): UseBlogPostReturn;
878
+ interface UseBlogCategoriesReturn {
879
+ data: BlogCategory[] | null;
880
+ isLoading: boolean;
881
+ error: ApiError$1 | null;
882
+ refetch: () => Promise<void>;
883
+ }
884
+ /**
885
+ * Fetch blog categories (for filters / navigation)
886
+ */
887
+ declare function useBlogCategories(): UseBlogCategoriesReturn;
888
+ interface UseBlogTagsReturn {
889
+ data: BlogTag[] | null;
890
+ isLoading: boolean;
891
+ error: ApiError$1 | null;
892
+ refetch: () => Promise<void>;
893
+ }
894
+ /**
895
+ * Fetch blog tags (for filters / display)
896
+ */
897
+ declare function useBlogTags(): UseBlogTagsReturn;
898
+ interface UseBlogCommentsReturn {
899
+ data: BlogComment[] | null;
900
+ isLoading: boolean;
901
+ error: ApiError$1 | null;
902
+ refetch: () => Promise<void>;
903
+ }
904
+ /**
905
+ * Fetch approved comments for a published blog post (public API)
906
+ *
907
+ * @example
908
+ * ```tsx
909
+ * const { data: comments, refetch } = useBlogComments(post?.slug);
910
+ * ```
911
+ */
912
+ declare function useBlogComments(slug: string | undefined | null): UseBlogCommentsReturn;
913
+ interface UseBlogCommentSubmitReturn {
914
+ submit: (payload: CreateBlogCommentPayload) => Promise<BlogComment | null>;
915
+ isSubmitting: boolean;
916
+ error: ApiError$1 | null;
917
+ resetError: () => void;
918
+ }
919
+ /**
920
+ * Submit a comment on a published blog post (public API). Call refetch on comments after success.
921
+ *
922
+ * @example
923
+ * ```tsx
924
+ * const { submit, isSubmitting, error } = useBlogCommentSubmit(post?.slug);
925
+ * await submit({ guestName: 'João', guestEmail: 'j@x.com', content: 'Texto' });
926
+ * refetch(); // from useBlogComments
927
+ * ```
928
+ */
929
+ declare function useBlogCommentSubmit(slug: string | undefined | null): UseBlogCommentSubmitReturn;
930
+ interface UseBlogFeaturedPostsReturn {
931
+ data: BlogPostPage | null;
932
+ isLoading: boolean;
933
+ error: ApiError$1 | null;
934
+ refetch: () => Promise<void>;
935
+ }
936
+ /**
937
+ * Fetch featured blog posts
938
+ */
939
+ declare function useBlogFeaturedPosts(limit?: number): UseBlogFeaturedPostsReturn;
940
+ interface UseNewsletterSubscribeReturn {
941
+ subscribe: (payload: SubscribeNewsletterPayload) => Promise<NewsletterSubscriber | null>;
942
+ isSubmitting: boolean;
943
+ error: ApiError$1 | null;
944
+ success: boolean;
945
+ reset: () => void;
946
+ }
947
+ /**
948
+ * Subscribe to newsletter (public API)
949
+ *
950
+ * @example
951
+ * ```tsx
952
+ * const { subscribe, isSubmitting, success, error } = useNewsletterSubscribe();
953
+ * await subscribe({ email: 'user@example.com', name: 'John' });
954
+ * ```
955
+ */
956
+ declare function useNewsletterSubscribe(): UseNewsletterSubscribeReturn;
957
+ interface UseNewsletterUnsubscribeReturn {
958
+ unsubscribe: (email: string) => Promise<boolean>;
959
+ /** Cancelar inscrição pelo token (link no email de confirmação). */
960
+ unsubscribeByToken: (token: string) => Promise<boolean>;
961
+ isSubmitting: boolean;
962
+ error: ApiError$1 | null;
963
+ success: boolean;
964
+ }
965
+ /**
966
+ * Unsubscribe from newsletter (public API)
967
+ */
968
+ declare function useNewsletterUnsubscribe(): UseNewsletterUnsubscribeReturn;
969
+
970
+ /**
971
+ * Admin hooks for Blog module (tenant-admin dashboard)
972
+ * These hooks use the authenticated admin API endpoints
973
+ */
974
+
975
+ interface CreateBlogPostPayload {
976
+ title: string;
977
+ slug?: string;
978
+ excerpt?: string;
979
+ content: string;
980
+ coverImageUrl?: string;
981
+ metaTitle?: string;
982
+ metaDescription?: string;
983
+ canonicalUrl?: string;
984
+ ogTitle?: string;
985
+ ogDescription?: string;
986
+ ogImageUrl?: string;
987
+ status?: 'DRAFT' | 'SCHEDULED' | 'PUBLISHED';
988
+ scheduledAt?: string;
989
+ isFeatured?: boolean;
990
+ allowComments?: boolean;
991
+ categoryIds?: string[];
992
+ tagIds?: string[];
993
+ isAiGenerated?: boolean;
994
+ aiGenerationMetadata?: Record<string, unknown>;
995
+ businessContextSnapshot?: string;
996
+ }
997
+ interface UpdateBlogPostPayload extends Partial<CreateBlogPostPayload> {
998
+ changeReason?: string;
999
+ }
1000
+ interface UseAdminBlogPostsOptions {
1001
+ page?: number;
1002
+ size?: number;
1003
+ }
1004
+ interface UseAdminBlogPostsReturn {
1005
+ data: BlogPostPage | null;
1006
+ isLoading: boolean;
1007
+ error: ApiError$1 | null;
1008
+ refetch: () => Promise<void>;
1009
+ }
1010
+ declare function useAdminBlogPosts(options?: UseAdminBlogPostsOptions): UseAdminBlogPostsReturn;
1011
+ interface UseAdminBlogPostReturn {
1012
+ data: BlogPost | null;
1013
+ isLoading: boolean;
1014
+ error: ApiError$1 | null;
1015
+ refetch: () => Promise<void>;
1016
+ }
1017
+ declare function useAdminBlogPost(id: string | undefined | null): UseAdminBlogPostReturn;
1018
+ interface UseAdminBlogPostMutationsReturn {
1019
+ create: (payload: CreateBlogPostPayload) => Promise<BlogPost | null>;
1020
+ update: (id: string, payload: UpdateBlogPostPayload) => Promise<BlogPost | null>;
1021
+ remove: (id: string) => Promise<boolean>;
1022
+ isLoading: boolean;
1023
+ error: ApiError$1 | null;
1024
+ }
1025
+ declare function useAdminBlogPostMutations(): UseAdminBlogPostMutationsReturn;
1026
+ interface CreateCategoryPayload {
1027
+ name: string;
1028
+ slug?: string;
1029
+ description?: string;
1030
+ color?: string;
1031
+ parentId?: string;
1032
+ sortOrder?: number;
1033
+ }
1034
+ interface UseAdminBlogCategoriesReturn {
1035
+ data: BlogCategory[] | null;
1036
+ isLoading: boolean;
1037
+ error: ApiError$1 | null;
1038
+ refetch: () => Promise<void>;
1039
+ create: (payload: CreateCategoryPayload) => Promise<BlogCategory | null>;
1040
+ update: (id: string, payload: Partial<CreateCategoryPayload>) => Promise<BlogCategory | null>;
1041
+ remove: (id: string) => Promise<boolean>;
1042
+ }
1043
+ declare function useAdminBlogCategories(): UseAdminBlogCategoriesReturn;
1044
+ interface CreateTagPayload {
1045
+ name: string;
1046
+ slug?: string;
1047
+ }
1048
+ interface UseAdminBlogTagsReturn {
1049
+ data: BlogTag[] | null;
1050
+ isLoading: boolean;
1051
+ error: ApiError$1 | null;
1052
+ refetch: () => Promise<void>;
1053
+ create: (payload: CreateTagPayload) => Promise<BlogTag | null>;
1054
+ update: (id: string, payload: Partial<CreateTagPayload>) => Promise<BlogTag | null>;
1055
+ remove: (id: string) => Promise<boolean>;
1056
+ }
1057
+ declare function useAdminBlogTags(): UseAdminBlogTagsReturn;
1058
+ interface UseAdminBlogCommentsOptions {
1059
+ status?: string;
1060
+ postId?: string;
1061
+ page?: number;
1062
+ size?: number;
1063
+ }
1064
+ interface BlogCommentAdmin extends BlogComment {
1065
+ postTitle?: string;
1066
+ postSlug?: string;
1067
+ authorEmail?: string;
1068
+ }
1069
+ interface BlogCommentPage {
1070
+ content: BlogCommentAdmin[];
1071
+ totalElements: number;
1072
+ totalPages: number;
1073
+ size: number;
1074
+ number: number;
1075
+ first: boolean;
1076
+ last: boolean;
1077
+ empty: boolean;
1078
+ }
1079
+ interface UseAdminBlogCommentsReturn {
1080
+ data: BlogCommentPage | null;
1081
+ isLoading: boolean;
1082
+ error: ApiError$1 | null;
1083
+ refetch: () => Promise<void>;
1084
+ updateStatus: (id: string, status: 'APPROVED' | 'REJECTED' | 'SPAM') => Promise<boolean>;
1085
+ remove: (id: string) => Promise<boolean>;
1086
+ }
1087
+ declare function useAdminBlogComments(options?: UseAdminBlogCommentsOptions): UseAdminBlogCommentsReturn;
1088
+ interface UseAdminNewsletterSubscribersOptions {
1089
+ status?: string;
1090
+ page?: number;
1091
+ size?: number;
1092
+ }
1093
+ interface UseAdminNewsletterSubscribersReturn {
1094
+ data: NewsletterSubscriberPage | null;
1095
+ isLoading: boolean;
1096
+ error: ApiError$1 | null;
1097
+ refetch: () => Promise<void>;
1098
+ remove: (id: string) => Promise<boolean>;
1099
+ }
1100
+ declare function useAdminNewsletterSubscribers(options?: UseAdminNewsletterSubscribersOptions): UseAdminNewsletterSubscribersReturn;
1101
+ interface UseAdminNewsletterStatsReturn {
1102
+ data: NewsletterStats | null;
1103
+ isLoading: boolean;
1104
+ error: ApiError$1 | null;
1105
+ refetch: () => Promise<void>;
1106
+ }
1107
+ declare function useAdminNewsletterStats(): UseAdminNewsletterStatsReturn;
1108
+ interface UseAdminBlogSettingsReturn {
1109
+ data: BlogSettings | null;
1110
+ isLoading: boolean;
1111
+ error: ApiError$1 | null;
1112
+ refetch: () => Promise<void>;
1113
+ update: (settings: Partial<BlogSettings>) => Promise<BlogSettings | null>;
1114
+ }
1115
+ declare function useAdminBlogSettings(): UseAdminBlogSettingsReturn;
1116
+ interface UseAdminBlogAnalyticsReturn {
1117
+ data: BlogAnalyticsSummary | null;
1118
+ isLoading: boolean;
1119
+ error: ApiError$1 | null;
1120
+ refetch: () => Promise<void>;
1121
+ }
1122
+ declare function useAdminBlogAnalytics(): UseAdminBlogAnalyticsReturn;
1123
+
1124
+ /**
1125
+ * Blog utilities - Schema.org JSON-LD for SEO
1126
+ */
1127
+
1128
+ interface BlogPostSchemaLdOptions {
1129
+ /** Full URL of the site (e.g. https://example.com) */
1130
+ siteUrl: string;
1131
+ /** Organization name for publisher */
1132
+ publisherName?: string;
1133
+ /** Logo URL for publisher */
1134
+ publisherLogoUrl?: string;
1135
+ }
1136
+ /**
1137
+ * Build Schema.org BlogPosting JSON-LD for a blog post
1138
+ *
1139
+ * @example
1140
+ * ```tsx
1141
+ * const schemaLd = getBlogPostSchemaLd(post, { siteUrl: 'https://example.com', publisherName: 'My Site' });
1142
+ * <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaLd) }} />
1143
+ * ```
1144
+ */
1145
+ declare function getBlogPostSchemaLd(post: BlogPost, options: BlogPostSchemaLdOptions): Record<string, unknown>;
1146
+
1147
+ export { type ApiError$1 as ApiError, AuthProvider, type BlogAnalyticsSummary, type BlogAuthor, type BlogCategory, type BlogComment, type BlogCommentAdmin, type BlogCommentPage, type BlogPost, type BlogPostPage, type BlogPostSchemaLdOptions, type BlogPostStatus, type BlogSettings, type BlogTag, type CreateBlogCommentPayload, type CreateBlogPostPayload, type CreateCategoryPayload, type CreateLeadRequest, type CreateTagPayload, Editable, EditableHTML, EditableImage, type EditableImageProps, type EditableProps, type EndUser, type EndUserLoginRequest, type FoxPixelConfig, FoxPixelHttpClient, FoxPixelProvider, GuestOnlyRoute, type Lead, type NewsletterFrequency, type NewsletterStats, type NewsletterStatus, type NewsletterSubscriber, type NewsletterSubscriberPage, type PostAnalyticsRow, ProtectedRoute, SITE_CONTENT_QUERY_KEY, type Service, type ServiceCatalogResponse, type SiteContent, type SubscribeNewsletterPayload, type UpdateBlogPostPayload, type UseAdminBlogAnalyticsReturn, type UseAdminBlogCategoriesReturn, type UseAdminBlogCommentsOptions, type UseAdminBlogCommentsReturn, type UseAdminBlogPostMutationsReturn, type UseAdminBlogPostReturn, type UseAdminBlogPostsOptions, type UseAdminBlogPostsReturn, type UseAdminBlogSettingsReturn, type UseAdminBlogTagsReturn, type UseAdminNewsletterStatsReturn, type UseAdminNewsletterSubscribersOptions, type UseAdminNewsletterSubscribersReturn, type UseBlogCategoriesReturn, type UseBlogCommentSubmitReturn, type UseBlogCommentsReturn, type UseBlogFeaturedPostsReturn, type UseBlogPostReturn, type UseBlogPostsOptions, type UseBlogPostsReturn, type UseBlogTagsReturn, type UseNewsletterSubscribeReturn, type UseNewsletterUnsubscribeReturn, type UseSiteContentOptions, type UseSiteContentQueryOptions, type UseSiteContentQueryReturn, type UseSiteContentReturn, type UseSiteContentSectionReturn, type UseSiteContentsOptions, type UseSiteContentsReturn, getBlogPostSchemaLd, useAdminBlogAnalytics, useAdminBlogCategories, useAdminBlogComments, useAdminBlogPost, useAdminBlogPostMutations, useAdminBlogPosts, useAdminBlogSettings, useAdminBlogTags, useAdminNewsletterStats, useAdminNewsletterSubscribers, useAuth, useBlogCategories, useBlogCommentSubmit, useBlogComments, useBlogFeaturedPosts, useBlogPost, useBlogPosts, useBlogTags, useContactCapture, useEditMode, useEditModeMessaging, useFoxPixelContext, useLeadCapture, useNewsletterSubscribe, useNewsletterUnsubscribe, useSendEditRequest, useServices, useSiteContent, useSiteContentQuery, useSiteContentSection, useSiteContents, withAuth };