@tribe-nest/forge 1.13.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/package.json +42 -0
- package/src/client/createForgeClient.ts +54 -0
- package/src/content/ForgeContentProvider.tsx +118 -0
- package/src/content/ForgeEditBridge.tsx +134 -0
- package/src/content/editFlag.ts +23 -0
- package/src/content/index.ts +28 -0
- package/src/content/resolve.ts +67 -0
- package/src/content/types.ts +97 -0
- package/src/content/useField.ts +64 -0
- package/src/contexts/AppAuthContext.tsx +185 -0
- package/src/contexts/AudioPlayerContext.tsx +389 -0
- package/src/contexts/CartContext.tsx +131 -0
- package/src/contexts/PublicAuthContext.tsx +333 -0
- package/src/contexts/useAppAdminGuard.ts +56 -0
- package/src/css.d.ts +5 -0
- package/src/data/collectionParams.ts +57 -0
- package/src/data/queries/useAccount.ts +71 -0
- package/src/data/queries/useAccountSettings.ts +82 -0
- package/src/data/queries/useAnalytics.ts +86 -0
- package/src/data/queries/useAuthActions.ts +28 -0
- package/src/data/queries/useBlog.ts +103 -0
- package/src/data/queries/useBlogComments.ts +96 -0
- package/src/data/queries/useBroadcasts.ts +31 -0
- package/src/data/queries/useCertificates.ts +58 -0
- package/src/data/queries/useChat.ts +267 -0
- package/src/data/queries/useCoachingAvailability.ts +69 -0
- package/src/data/queries/useCoachingProducts.ts +67 -0
- package/src/data/queries/useCohortPage.ts +17 -0
- package/src/data/queries/useCollections.ts +211 -0
- package/src/data/queries/useCommunity.ts +794 -0
- package/src/data/queries/useCoupons.ts +20 -0
- package/src/data/queries/useCourseAccess.ts +50 -0
- package/src/data/queries/useCourses.ts +91 -0
- package/src/data/queries/useCurrencies.ts +33 -0
- package/src/data/queries/useDocuments.ts +208 -0
- package/src/data/queries/useDonations.ts +29 -0
- package/src/data/queries/useEngagement.ts +115 -0
- package/src/data/queries/useEvents.ts +69 -0
- package/src/data/queries/useFinalize.ts +109 -0
- package/src/data/queries/useForms.ts +31 -0
- package/src/data/queries/useInvoice.ts +65 -0
- package/src/data/queries/useLeadMagnet.ts +96 -0
- package/src/data/queries/useMembership.ts +20 -0
- package/src/data/queries/useNotifications.ts +267 -0
- package/src/data/queries/useOffers.ts +56 -0
- package/src/data/queries/useOrders.ts +71 -0
- package/src/data/queries/usePWA.ts +101 -0
- package/src/data/queries/usePageActions.ts +36 -0
- package/src/data/queries/usePaymentFlow.ts +80 -0
- package/src/data/queries/usePaymentLink.ts +47 -0
- package/src/data/queries/usePodcast.ts +85 -0
- package/src/data/queries/usePostCollections.ts +48 -0
- package/src/data/queries/usePosts.ts +94 -0
- package/src/data/queries/useProducts.ts +108 -0
- package/src/data/queries/useReplay.ts +19 -0
- package/src/data/queries/useReviews.ts +122 -0
- package/src/data/queries/useShipping.ts +109 -0
- package/src/data/queries/useSubscriptions.ts +66 -0
- package/src/data/queries/useWebsite.ts +128 -0
- package/src/data/queries/useWebsiteAgent.ts +100 -0
- package/src/index.ts +98 -0
- package/src/provider/ForgeAppProvider.tsx +149 -0
- package/src/provider/ForgeProvider.tsx +91 -0
- package/src/provider/SiteConfigProvider.tsx +27 -0
- package/src/server/_tests/buildPwaHead.spec.ts +182 -0
- package/src/server/_tests/buildWebManifest.spec.ts +313 -0
- package/src/server/_tests/forgeServiceWorkerScript.spec.ts +251 -0
- package/src/server/index.ts +260 -0
- package/src/server/jobs.ts +104 -0
- package/src/server/pwa.ts +332 -0
- package/src/types/index.ts +74 -0
- package/src/types/models.ts +1313 -0
- package/src/ui/analytics/ForgeAnalytics.tsx +208 -0
- package/src/ui/analytics/PageMetaPixel.tsx +92 -0
- package/src/ui/content/Editable.tsx +90 -0
- package/src/ui/format/PriceDisplay.tsx +142 -0
- package/src/ui/format/useFormatCurrency.ts +92 -0
- package/src/ui/headless/agent/useAiAgent.ts +106 -0
- package/src/ui/headless/auth/useLoginFlow.ts +72 -0
- package/src/ui/headless/auth/useSignupForm.ts +85 -0
- package/src/ui/headless/broadcast/useBroadcastWatch.ts +59 -0
- package/src/ui/headless/chat/attachmentKind.ts +27 -0
- package/src/ui/headless/chat/useChatAttachmentUpload.ts +132 -0
- package/src/ui/headless/chat/useChatRoom.ts +223 -0
- package/src/ui/headless/chat/useMessageThread.ts +60 -0
- package/src/ui/headless/checkout/useCheckout.ts +438 -0
- package/src/ui/headless/coaching/useCoachingBooking.ts +223 -0
- package/src/ui/headless/community/index.ts +20 -0
- package/src/ui/headless/community/lexical.ts +131 -0
- package/src/ui/headless/community/replyTree.ts +51 -0
- package/src/ui/headless/community/richLexical.ts +314 -0
- package/src/ui/headless/community/useCommunityAttachmentUpload.ts +137 -0
- package/src/ui/headless/community/useCommunitySpaceFeed.ts +122 -0
- package/src/ui/headless/community/useCommunityThread.ts +95 -0
- package/src/ui/headless/community/useMentionComposer.ts +105 -0
- package/src/ui/headless/community/useRichMentionComposer.ts +193 -0
- package/src/ui/headless/consent/useCookieConsent.ts +81 -0
- package/src/ui/headless/course/useCourseCheckout.ts +99 -0
- package/src/ui/headless/dialog.tsx +14 -0
- package/src/ui/headless/document/index.ts +7 -0
- package/src/ui/headless/document/useContractSignature.ts +68 -0
- package/src/ui/headless/document/useDocumentSigningFlow.ts +92 -0
- package/src/ui/headless/document/useOtpCodeInput.ts +86 -0
- package/src/ui/headless/document/useSignaturePad.ts +180 -0
- package/src/ui/headless/donation/Donation.tsx +115 -0
- package/src/ui/headless/donation/DonationContext.tsx +223 -0
- package/src/ui/headless/donation/fees.ts +15 -0
- package/src/ui/headless/donation/index.ts +10 -0
- package/src/ui/headless/engagement/usePostEngagement.ts +119 -0
- package/src/ui/headless/event/useEventCheckout.ts +141 -0
- package/src/ui/headless/forms/useContactForm.ts +58 -0
- package/src/ui/headless/forms/useEmailListForm.ts +75 -0
- package/src/ui/headless/forms/useSectionedForm.ts +256 -0
- package/src/ui/headless/index.ts +60 -0
- package/src/ui/headless/invoice/useInvoicePayment.ts +48 -0
- package/src/ui/headless/memberHome/index.ts +7 -0
- package/src/ui/headless/memberHome/useMemberHomeNav.ts +82 -0
- package/src/ui/headless/membership/MembershipGate.tsx +39 -0
- package/src/ui/headless/membership/useMembershipCheckout.ts +121 -0
- package/src/ui/headless/offer/Offer.tsx +88 -0
- package/src/ui/headless/offer/OfferContext.tsx +162 -0
- package/src/ui/headless/offer/index.ts +8 -0
- package/src/ui/headless/paymentLink/usePaymentLinkPayment.ts +46 -0
- package/src/ui/headless/podcast/usePodcastPlayer.ts +96 -0
- package/src/ui/headless/posts/index.ts +13 -0
- package/src/ui/headless/posts/upsell.ts +41 -0
- package/src/ui/headless/posts/useMemberPostsFeed.ts +110 -0
- package/src/ui/headless/posts/usePostCollectionDetail.ts +56 -0
- package/src/ui/headless/posts/usePostCollectionsList.ts +90 -0
- package/src/ui/headless/posts/usePostItem.ts +81 -0
- package/src/ui/headless/reviews/index.ts +6 -0
- package/src/ui/headless/reviews/useHelpfulVote.ts +45 -0
- package/src/ui/headless/reviews/useReviewForm.ts +108 -0
- package/src/ui/headless/reviews/useReviewsList.ts +79 -0
- package/src/ui/headless/work/index.ts +25 -0
- package/src/ui/headless/work/useWorkPortal.ts +349 -0
- package/src/ui/index.ts +144 -0
- package/src/ui/payment/ForgePaymentProvider.tsx +45 -0
- package/src/ui/payment/ForgeStripePayment.tsx +171 -0
- package/src/ui/shell/TribeNestApp.tsx +121 -0
- package/src/ui/shell/shellGating.spec.ts +28 -0
- package/src/ui/shell/shellGating.ts +11 -0
- package/src/ui/styled/AccountDashboard.tsx +575 -0
- package/src/ui/styled/AiAgentWidget.tsx +152 -0
- package/src/ui/styled/AudioPlayer.tsx +123 -0
- package/src/ui/styled/BlogCategory.tsx +79 -0
- package/src/ui/styled/BlogComments.tsx +236 -0
- package/src/ui/styled/BlogList.tsx +101 -0
- package/src/ui/styled/BlogPost.tsx +160 -0
- package/src/ui/styled/BlogPostFooter.tsx +33 -0
- package/src/ui/styled/BotProtection.tsx +101 -0
- package/src/ui/styled/Button.tsx +88 -0
- package/src/ui/styled/Cart.tsx +276 -0
- package/src/ui/styled/ChatRoom.tsx +252 -0
- package/src/ui/styled/Checkout.tsx +988 -0
- package/src/ui/styled/CheckoutConfirmation.tsx +452 -0
- package/src/ui/styled/CoachingBooking.tsx +605 -0
- package/src/ui/styled/CoachingConfirmation.tsx +151 -0
- package/src/ui/styled/CoachingDetail.tsx +99 -0
- package/src/ui/styled/CohortPage.tsx +82 -0
- package/src/ui/styled/ConfirmSubscription.tsx +134 -0
- package/src/ui/styled/Confirmation.tsx +171 -0
- package/src/ui/styled/ContactForm.tsx +81 -0
- package/src/ui/styled/CookieConsent.tsx +239 -0
- package/src/ui/styled/CourseAccess.tsx +169 -0
- package/src/ui/styled/CourseCheckout.tsx +425 -0
- package/src/ui/styled/CourseConfirmation.tsx +191 -0
- package/src/ui/styled/CourseDetail.tsx +140 -0
- package/src/ui/styled/CoursesGrid.tsx +85 -0
- package/src/ui/styled/CurrencySwitcher.tsx +51 -0
- package/src/ui/styled/DonationButton.tsx +247 -0
- package/src/ui/styled/DonationPage.tsx +29 -0
- package/src/ui/styled/EmailListForm.tsx +88 -0
- package/src/ui/styled/EventConfirmation.tsx +228 -0
- package/src/ui/styled/EventCountdown.tsx +98 -0
- package/src/ui/styled/EventDetail.tsx +141 -0
- package/src/ui/styled/EventTickets.tsx +548 -0
- package/src/ui/styled/EventsList.tsx +83 -0
- package/src/ui/styled/ForgotPasswordForm.tsx +105 -0
- package/src/ui/styled/FormRenderer.tsx +249 -0
- package/src/ui/styled/InstallBanner.tsx +114 -0
- package/src/ui/styled/InvoiceConfirmation.tsx +50 -0
- package/src/ui/styled/InvoicePayment.tsx +141 -0
- package/src/ui/styled/LeadMagnet.tsx +93 -0
- package/src/ui/styled/Loading.tsx +56 -0
- package/src/ui/styled/LoginForm.tsx +134 -0
- package/src/ui/styled/Markdown.tsx +103 -0
- package/src/ui/styled/MembershipCheckout.tsx +197 -0
- package/src/ui/styled/MembershipTiers.tsx +115 -0
- package/src/ui/styled/OfferButton.tsx +181 -0
- package/src/ui/styled/PageActions.tsx +124 -0
- package/src/ui/styled/PaymentLinkConfirmation.tsx +50 -0
- package/src/ui/styled/PaymentLinkPayment.tsx +85 -0
- package/src/ui/styled/Paywall.tsx +54 -0
- package/src/ui/styled/PlayButton.tsx +48 -0
- package/src/ui/styled/PodcastEpisode.tsx +137 -0
- package/src/ui/styled/PodcastList.tsx +58 -0
- package/src/ui/styled/PodcastShow.tsx +164 -0
- package/src/ui/styled/PostsFeed.tsx +63 -0
- package/src/ui/styled/ProductDetail.tsx +616 -0
- package/src/ui/styled/ProductGrid.tsx +82 -0
- package/src/ui/styled/PushOptIn.tsx +65 -0
- package/src/ui/styled/PwaRegistration.tsx +58 -0
- package/src/ui/styled/ReactionBar.tsx +93 -0
- package/src/ui/styled/ReplayList.tsx +134 -0
- package/src/ui/styled/ResetPasswordForm.tsx +127 -0
- package/src/ui/styled/ReviewForm.tsx +292 -0
- package/src/ui/styled/ReviewsSection.tsx +260 -0
- package/src/ui/styled/SectionedFormRenderer.tsx +146 -0
- package/src/ui/styled/SignupForm.tsx +109 -0
- package/src/ui/styled/UserMenu.tsx +166 -0
- package/src/ui/styled/chat/ChatAttachments.tsx +172 -0
- package/src/ui/styled/chat/ChatComposer.tsx +153 -0
- package/src/ui/styled/chat/ChatThreadPanel.tsx +94 -0
- package/src/ui/styled/chat/MessageBubble.tsx +66 -0
- package/src/ui/styled/chat/NewDmDialog.tsx +78 -0
- package/src/ui/styled/community/CommunityComposer.tsx +358 -0
- package/src/ui/styled/community/CommunityFeed.tsx +483 -0
- package/src/ui/styled/community/CommunityLeaderboard.tsx +109 -0
- package/src/ui/styled/community/CommunityMediaGallery.tsx +116 -0
- package/src/ui/styled/community/CommunityMemberProfile.tsx +285 -0
- package/src/ui/styled/community/CommunityNotifications.tsx +222 -0
- package/src/ui/styled/community/CommunityPostCard.tsx +165 -0
- package/src/ui/styled/community/CommunityPostDetail.tsx +337 -0
- package/src/ui/styled/community/CommunityReactionBar.tsx +107 -0
- package/src/ui/styled/community/CommunityRichText.tsx +157 -0
- package/src/ui/styled/community/CommunitySpaces.tsx +174 -0
- package/src/ui/styled/community/MemberAvatar.tsx +74 -0
- package/src/ui/styled/community/index.ts +23 -0
- package/src/ui/styled/community/util.ts +15 -0
- package/src/ui/styled/members/MemberHomeLayout.tsx +98 -0
- package/src/ui/styled/members/MemberPostCard.tsx +392 -0
- package/src/ui/styled/members/MemberPostsFeed.tsx +219 -0
- package/src/ui/styled/members/PostCollectionDetail.tsx +227 -0
- package/src/ui/styled/members/PostCollectionsGrid.tsx +287 -0
- package/src/ui/styled/members/index.ts +7 -0
- package/src/ui/styled/rich-text.css +150 -0
- package/src/ui/styled/work/WorkInviteAccept.tsx +222 -0
- package/src/ui/styled/work/WorkPortalProjects.tsx +163 -0
- package/src/ui/styled/work/WorkProjectInvoices.tsx +113 -0
- package/src/ui/styled/work/WorkProjectReport.tsx +66 -0
- package/src/ui/styled/work/WorkReportBody.tsx +327 -0
- package/src/ui/styled/work/WorkTaskAttachments.tsx +67 -0
- package/src/ui/styled/work/WorkTaskComments.tsx +151 -0
- package/src/ui/styled/work/WorkTaskDetail.tsx +92 -0
- package/src/ui/styled/work/WorkTokenReport.tsx +49 -0
- package/src/ui/styled/work/index.ts +10 -0
- package/src/ui/theme/ForgeThemeProvider.tsx +121 -0
- package/src/ui/theme/contrast.ts +22 -0
- package/src/utils/analyticsBus.ts +31 -0
- package/src/utils/attribution.ts +68 -0
- package/src/utils/cookieConsent.ts +83 -0
- package/src/utils/landing.ts +153 -0
- package/src/utils/metaPixel.ts +122 -0
- package/src/utils/structuredData.ts +126 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { CoachingProduct, PaginatedData } from "../../types/models";
|
|
2
|
+
import { useForge } from "../../provider/ForgeProvider";
|
|
3
|
+
import { useQuery } from "@tanstack/react-query";
|
|
4
|
+
|
|
5
|
+
export interface GetCoachingProductsParams {
|
|
6
|
+
page?: number;
|
|
7
|
+
query?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function useGetCoachingProducts(params?: GetCoachingProductsParams, enabled = true) {
|
|
11
|
+
const { client, profileId } = useForge();
|
|
12
|
+
|
|
13
|
+
return useQuery<PaginatedData<CoachingProduct>>({
|
|
14
|
+
queryKey: ["coaching-products", profileId, params],
|
|
15
|
+
queryFn: async () => {
|
|
16
|
+
const res = await client.get("/public/coaching/products", {
|
|
17
|
+
params: {
|
|
18
|
+
profileId: profileId,
|
|
19
|
+
page: params?.page || 1,
|
|
20
|
+
limit: 10,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
return res.data;
|
|
24
|
+
},
|
|
25
|
+
enabled: !!profileId && !!client && enabled,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function useGetCoachingProduct(coachingProductId?: string, options?: { initialData?: CoachingProduct }) {
|
|
30
|
+
const { client, profileId } = useForge();
|
|
31
|
+
|
|
32
|
+
return useQuery<CoachingProduct>({
|
|
33
|
+
queryKey: ["coaching-product", profileId, { coachingProductId }],
|
|
34
|
+
queryFn: async () => {
|
|
35
|
+
const res = await client.get(`/public/coaching/products/${coachingProductId}`, {
|
|
36
|
+
params: {
|
|
37
|
+
profileId: profileId,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
return res.data;
|
|
41
|
+
},
|
|
42
|
+
enabled: !!profileId && !!client && !!coachingProductId,
|
|
43
|
+
initialData: options?.initialData,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function useGetCoachingProductsByIds(coachingProductIds: string[]) {
|
|
48
|
+
const { client, profileId } = useForge();
|
|
49
|
+
const idsKey = coachingProductIds.join(",");
|
|
50
|
+
|
|
51
|
+
return useQuery<CoachingProduct[]>({
|
|
52
|
+
queryKey: ["coaching-products", "batch", profileId, idsKey],
|
|
53
|
+
queryFn: async () => {
|
|
54
|
+
const res = await client.get("/public/coaching/products/batch", {
|
|
55
|
+
params: {
|
|
56
|
+
profileId: profileId,
|
|
57
|
+
ids: idsKey,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
const data = res.data as CoachingProduct[];
|
|
61
|
+
return coachingProductIds
|
|
62
|
+
.map((id) => data.find((p) => p.id === id))
|
|
63
|
+
.filter((p): p is CoachingProduct => p != null);
|
|
64
|
+
},
|
|
65
|
+
enabled: !!profileId && !!client && coachingProductIds.length > 0,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CohortPagePayload } from "../../types/models";
|
|
2
|
+
import { useForge } from "../../provider/ForgeProvider";
|
|
3
|
+
import { useQuery } from "@tanstack/react-query";
|
|
4
|
+
|
|
5
|
+
/** The public cohort landing page payload (data + serialized craft layout). */
|
|
6
|
+
export function useCohortPage(cohortId?: string) {
|
|
7
|
+
const { client } = useForge();
|
|
8
|
+
|
|
9
|
+
return useQuery<CohortPagePayload>({
|
|
10
|
+
queryKey: ["public-cohort-page", cohortId],
|
|
11
|
+
queryFn: async () => {
|
|
12
|
+
const res = await client.get(`/public/cohorts/${cohortId}/page`);
|
|
13
|
+
return res.data;
|
|
14
|
+
},
|
|
15
|
+
enabled: !!client && !!cohortId,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Collection,
|
|
3
|
+
CollectionEntry,
|
|
4
|
+
CollectionQueryParams,
|
|
5
|
+
CollectionQuery,
|
|
6
|
+
CollectionAggregate,
|
|
7
|
+
CollectionAggregateBucket,
|
|
8
|
+
CollectionSearchResult,
|
|
9
|
+
} from "../../types/models";
|
|
10
|
+
import { useForge } from "../../provider/ForgeProvider";
|
|
11
|
+
import { useQuery, useMutation } from "@tanstack/react-query";
|
|
12
|
+
import { collectionParamsToQuery, collectionParamsKey, splitCollectionQuery, collectionQueryKey } from "../collectionParams";
|
|
13
|
+
|
|
14
|
+
// Generic data hooks for CMS collections. Collection-agnostic — a lawyers
|
|
15
|
+
// directory and a recipe index use the same three hooks. The bespoke rendering
|
|
16
|
+
// lives in the (agent-authored) site routes; these just fetch schema + entries.
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A collection's definition + ordered field schema, by slug. Use it to drive
|
|
20
|
+
* bespoke rendering (read `.fields` for keys/types/sections).
|
|
21
|
+
*/
|
|
22
|
+
export function useCollection(slug?: string, options?: { initialData?: Collection | null }) {
|
|
23
|
+
const { client, profileId } = useForge();
|
|
24
|
+
|
|
25
|
+
return useQuery<Collection | null>({
|
|
26
|
+
queryKey: ["collection-schema", profileId, slug],
|
|
27
|
+
queryFn: async () => {
|
|
28
|
+
const res = await client.get(`/public/collections/${slug}/schema`, {
|
|
29
|
+
params: { profileId },
|
|
30
|
+
});
|
|
31
|
+
return res.data;
|
|
32
|
+
},
|
|
33
|
+
enabled: !!profileId && !!client && !!slug,
|
|
34
|
+
initialData: options?.initialData,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Search/filter/sort/geo over a collection's published entries (GET /entries,
|
|
40
|
+
* lenient). Which fields accept filter/sort is creator-controlled — read
|
|
41
|
+
* `useCollection(slug).fields` for the queryable set.
|
|
42
|
+
*
|
|
43
|
+
* @deprecated Prefer {@link useCollectionQuery}, which runs the full query
|
|
44
|
+
* engine (reference `expand`, `select`, multi-key sort, cursor, `mine` scope)
|
|
45
|
+
* and validates strictly. `useCollectionEntries` stays for backwards
|
|
46
|
+
* compatibility with existing sites and won't be removed.
|
|
47
|
+
*/
|
|
48
|
+
export function useCollectionEntries(
|
|
49
|
+
slug?: string,
|
|
50
|
+
params?: CollectionQueryParams,
|
|
51
|
+
options?: { initialData?: CollectionSearchResult },
|
|
52
|
+
) {
|
|
53
|
+
const { client, profileId } = useForge();
|
|
54
|
+
|
|
55
|
+
return useQuery<CollectionSearchResult>({
|
|
56
|
+
queryKey: ["collection-entries", profileId, slug, collectionParamsKey(params)],
|
|
57
|
+
queryFn: async () => {
|
|
58
|
+
const res = await client.get(`/public/collections/${slug}/entries`, {
|
|
59
|
+
params: collectionParamsToQuery(profileId, params),
|
|
60
|
+
});
|
|
61
|
+
return res.data;
|
|
62
|
+
},
|
|
63
|
+
enabled: !!profileId && !!client && !!slug,
|
|
64
|
+
initialData: options?.initialData,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Rich, structured query over a collection via the query engine (POST /query):
|
|
70
|
+
* the full operator set + reference `expand`, `select`, multi-key `sort`, cursor
|
|
71
|
+
* pagination, facets, and `scope: "mine"` (the logged-in member's own rows,
|
|
72
|
+
* drafts included). STRICT — an undeclared field / wrong-kind operator errors,
|
|
73
|
+
* so it catches mistakes. Additive to `useCollectionEntries`, which is unchanged.
|
|
74
|
+
*/
|
|
75
|
+
export function useCollectionQuery(
|
|
76
|
+
slug?: string,
|
|
77
|
+
query?: CollectionQuery,
|
|
78
|
+
options?: { initialData?: CollectionSearchResult; enabled?: boolean },
|
|
79
|
+
) {
|
|
80
|
+
const { client, profileId } = useForge();
|
|
81
|
+
const { scope, query: body } = splitCollectionQuery(query);
|
|
82
|
+
|
|
83
|
+
return useQuery<CollectionSearchResult>({
|
|
84
|
+
queryKey: ["collection-query", profileId, slug, collectionQueryKey(query)],
|
|
85
|
+
queryFn: async () => {
|
|
86
|
+
const res = await client.post(`/public/collections/${slug}/query`, { profileId, scope, query: body });
|
|
87
|
+
return res.data;
|
|
88
|
+
},
|
|
89
|
+
enabled: (options?.enabled ?? true) && !!profileId && !!client && !!slug,
|
|
90
|
+
initialData: options?.initialData,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Aggregate a collection (group-by + count/sum/avg/min/max) via POST /aggregate —
|
|
96
|
+
* totals, leaderboards, "washes remaining". `scope: "mine"` restricts to the
|
|
97
|
+
* member's own rows.
|
|
98
|
+
*/
|
|
99
|
+
export function useCollectionAggregate(
|
|
100
|
+
slug?: string,
|
|
101
|
+
aggregate?: CollectionAggregate,
|
|
102
|
+
opts?: { scope?: "public" | "mine"; where?: CollectionQuery["where"]; search?: string; enabled?: boolean },
|
|
103
|
+
) {
|
|
104
|
+
const { client, profileId } = useForge();
|
|
105
|
+
|
|
106
|
+
return useQuery<CollectionAggregateBucket[]>({
|
|
107
|
+
queryKey: ["collection-aggregate", profileId, slug, JSON.stringify({ aggregate, ...opts })],
|
|
108
|
+
queryFn: async () => {
|
|
109
|
+
const res = await client.post(`/public/collections/${slug}/aggregate`, {
|
|
110
|
+
profileId,
|
|
111
|
+
scope: opts?.scope,
|
|
112
|
+
where: opts?.where,
|
|
113
|
+
search: opts?.search,
|
|
114
|
+
aggregate,
|
|
115
|
+
});
|
|
116
|
+
return res.data.data;
|
|
117
|
+
},
|
|
118
|
+
enabled: (opts?.enabled ?? true) && !!profileId && !!client && !!slug && !!aggregate,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export type SubmitCollectionEntryInput = {
|
|
123
|
+
data: Record<string, unknown>;
|
|
124
|
+
/** For anonymous submissions, when the collection requires them. */
|
|
125
|
+
name?: string;
|
|
126
|
+
email?: string;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export type SubmitCollectionEntryResult = { id: string; slug: string; status: "draft" | "published" };
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Submit a new entry to a collection that has public submissions enabled
|
|
133
|
+
* (e.g. leave a review). Members are authenticated via the Forge token; the
|
|
134
|
+
* result's `status` is "published" (auto) or "draft" (held for review).
|
|
135
|
+
*/
|
|
136
|
+
export function useSubmitCollectionEntry(slug?: string) {
|
|
137
|
+
const { client, profileId } = useForge();
|
|
138
|
+
|
|
139
|
+
return useMutation<SubmitCollectionEntryResult, unknown, SubmitCollectionEntryInput>({
|
|
140
|
+
mutationFn: async (input) => {
|
|
141
|
+
const res = await client.post(`/public/collections/${slug}/entries`, {
|
|
142
|
+
profileId,
|
|
143
|
+
data: input.data,
|
|
144
|
+
name: input.name,
|
|
145
|
+
email: input.email,
|
|
146
|
+
});
|
|
147
|
+
return res.data;
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// --- owner-scoped writes (member-owned collections) ---------------------------
|
|
153
|
+
// For a `user`-owned collection, a signed-in member manages THEIR OWN rows. The
|
|
154
|
+
// owner + profile come from the member's token, so these send neither.
|
|
155
|
+
|
|
156
|
+
export type MyEntryInput = { data: Record<string, unknown>; status?: "draft" | "published" };
|
|
157
|
+
export type MyEntryResult = { id: string; slug?: string; status: "draft" | "published" };
|
|
158
|
+
|
|
159
|
+
/** Create one of the member's own entries in a user-owned collection. */
|
|
160
|
+
export function useCreateMyEntry(slug?: string) {
|
|
161
|
+
const { client } = useForge();
|
|
162
|
+
return useMutation<MyEntryResult, unknown, MyEntryInput>({
|
|
163
|
+
mutationFn: async (input) => {
|
|
164
|
+
const res = await client.post(`/public/collections/${slug}/my-entries`, input);
|
|
165
|
+
return res.data;
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Update one of the member's own entries. */
|
|
171
|
+
export function useUpdateMyEntry(slug?: string) {
|
|
172
|
+
const { client } = useForge();
|
|
173
|
+
return useMutation<MyEntryResult, unknown, { entryId: string } & MyEntryInput>({
|
|
174
|
+
mutationFn: async ({ entryId, ...input }) => {
|
|
175
|
+
const res = await client.put(`/public/collections/${slug}/my-entries/${entryId}`, input);
|
|
176
|
+
return res.data;
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Delete one of the member's own entries. */
|
|
182
|
+
export function useDeleteMyEntry(slug?: string) {
|
|
183
|
+
const { client } = useForge();
|
|
184
|
+
return useMutation<{ success: boolean }, unknown, { entryId: string }>({
|
|
185
|
+
mutationFn: async ({ entryId }) => {
|
|
186
|
+
const res = await client.delete(`/public/collections/${slug}/my-entries/${entryId}`);
|
|
187
|
+
return res.data;
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** A single published entry by slug. */
|
|
193
|
+
export function useCollectionEntry(
|
|
194
|
+
slug?: string,
|
|
195
|
+
entrySlug?: string,
|
|
196
|
+
options?: { initialData?: CollectionEntry | null },
|
|
197
|
+
) {
|
|
198
|
+
const { client, profileId } = useForge();
|
|
199
|
+
|
|
200
|
+
return useQuery<CollectionEntry | null>({
|
|
201
|
+
queryKey: ["collection-entry", profileId, slug, entrySlug],
|
|
202
|
+
queryFn: async () => {
|
|
203
|
+
const res = await client.get(`/public/collections/${slug}/entries/${entrySlug}`, {
|
|
204
|
+
params: { profileId },
|
|
205
|
+
});
|
|
206
|
+
return res.data;
|
|
207
|
+
},
|
|
208
|
+
enabled: !!profileId && !!client && !!slug && !!entrySlug,
|
|
209
|
+
initialData: options?.initialData,
|
|
210
|
+
});
|
|
211
|
+
}
|