From de7870ea461ef975ae46aa42454af1cee8ef7732 Mon Sep 17 00:00:00 2001 From: capJavert Date: Wed, 10 Dec 2025 16:00:59 +0100 Subject: [PATCH 1/5] feat: job payment --- .../RecruiterPaymentContext.tsx | 15 ++++ .../RecruiterPaymentPaddleContext.tsx | 78 +++++++++++++++++++ .../contexts/RecruiterPaymentContext/types.ts | 37 +++++++++ .../src/features/opportunity/graphql.ts | 25 ++++++ .../shared/src/features/opportunity/types.ts | 1 + packages/shared/src/graphql/paddle.ts | 1 + packages/shared/src/hooks/usePaddlePayment.ts | 6 +- .../recruiter/[opportunityId]/analyze.tsx | 21 ++++- .../{ => [opportunityId]}/payment.tsx | 16 ++-- 9 files changed, 185 insertions(+), 15 deletions(-) create mode 100644 packages/shared/src/contexts/RecruiterPaymentContext/RecruiterPaymentContext.tsx create mode 100644 packages/shared/src/contexts/RecruiterPaymentContext/RecruiterPaymentPaddleContext.tsx create mode 100644 packages/shared/src/contexts/RecruiterPaymentContext/types.ts rename packages/webapp/pages/recruiter/{ => [opportunityId]}/payment.tsx (90%) diff --git a/packages/shared/src/contexts/RecruiterPaymentContext/RecruiterPaymentContext.tsx b/packages/shared/src/contexts/RecruiterPaymentContext/RecruiterPaymentContext.tsx new file mode 100644 index 00000000000..dc292dca9b4 --- /dev/null +++ b/packages/shared/src/contexts/RecruiterPaymentContext/RecruiterPaymentContext.tsx @@ -0,0 +1,15 @@ +import type { ReactElement } from 'react'; +import React from 'react'; +import type { RecruiterContextProviderProps } from './types'; +import { RecruiterPaymentPaddleContextProvider } from './RecruiterPaymentPaddleContext'; + +export const RecruiterPaymentContext = ({ + children, + ...props +}: RecruiterContextProviderProps): ReactElement => { + return ( + + {children} + + ); +}; diff --git a/packages/shared/src/contexts/RecruiterPaymentContext/RecruiterPaymentPaddleContext.tsx b/packages/shared/src/contexts/RecruiterPaymentContext/RecruiterPaymentPaddleContext.tsx new file mode 100644 index 00000000000..7090858c885 --- /dev/null +++ b/packages/shared/src/contexts/RecruiterPaymentContext/RecruiterPaymentPaddleContext.tsx @@ -0,0 +1,78 @@ +import type { ReactElement } from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import { useRouter } from 'next/router'; +import { PurchaseType } from '../../graphql/paddle'; +import { usePaddlePayment } from '../../hooks/usePaddlePayment'; +import { useLogContext } from '../LogContext'; +import { useAuthContext } from '../AuthContext'; +import type { + RecruiterPaymentContextData, + RecruiterContextProviderProps, + RecruiterProductOption, +} from './types'; +import { RecruiterPaymentContext } from './types'; +import { webappUrl } from '../../lib/constants'; +import { recruiterPricesQueryOptions } from '../../features/opportunity/graphql'; + +export const RecruiterPaymentPaddleContextProvider = ({ + onCompletion, + origin, + children, +}: RecruiterContextProviderProps): ReactElement => { + const router = useRouter(); + const { user, isLoggedIn } = useAuthContext(); + const { logEvent } = useLogContext(); + const [selectedProduct, setSelectedProduct] = + useState(); + const logRef = useRef(); + logRef.current = logEvent; + + const { data: prices } = useQuery( + recruiterPricesQueryOptions({ + user, + isLoggedIn, + }), + ); + + useEffect(() => { + if (!prices?.length) { + return; + } + + if (selectedProduct) { + return; + } + + setSelectedProduct({ + id: prices[0].priceId, + }); + }, [prices, selectedProduct]); + + const { paddle, openCheckout } = usePaddlePayment({ + successCallback: () => { + router.push( + `${webappUrl}recruiter/${router.query.opportunityId}/prepare`, + ); + }, + priceType: PurchaseType.Recruiter, + }); + + const contextData = useMemo( + () => ({ + paddle, + onCompletion, + selectedProduct, + setSelectedProduct, + openCheckout, + origin, + }), + [onCompletion, openCheckout, origin, paddle, selectedProduct], + ); + + return ( + + {children} + + ); +}; diff --git a/packages/shared/src/contexts/RecruiterPaymentContext/types.ts b/packages/shared/src/contexts/RecruiterPaymentContext/types.ts new file mode 100644 index 00000000000..8ebc23e8c65 --- /dev/null +++ b/packages/shared/src/contexts/RecruiterPaymentContext/types.ts @@ -0,0 +1,37 @@ +import type { Paddle } from '@paddle/paddle-js'; +import type { ReactNode } from 'react'; +import { createContext, useContext } from 'react'; +import type { OpenCheckoutFn } from '../payment/context'; +import type { Origin } from '../../lib/log'; + +export const RecruiterPaymentContext = + createContext(undefined); + +export const useRecruiterPaymentContext = (): RecruiterPaymentContextData => + useContext(RecruiterPaymentContext); + +export type RecruiterProductOption = { + id: string; +}; + +export type ProcessingError = { + title: string; + description?: string; + onRequestClose?: () => void; +}; + +export type RecruiterPaymentContextData = { + paddle?: Paddle | undefined; + openCheckout?: OpenCheckoutFn; + onCompletion?: () => void; + selectedProduct?: RecruiterProductOption; + setSelectedProduct: (product: RecruiterProductOption) => void; + error?: ProcessingError; + origin?: Origin; +}; + +export type RecruiterContextProviderProps = { + children?: ReactNode; + origin?: Origin; + onCompletion?: () => void; +}; diff --git a/packages/shared/src/features/opportunity/graphql.ts b/packages/shared/src/features/opportunity/graphql.ts index 89d5712a862..7d2efd606ac 100644 --- a/packages/shared/src/features/opportunity/graphql.ts +++ b/packages/shared/src/features/opportunity/graphql.ts @@ -1,5 +1,8 @@ import { gql } from 'graphql-request'; import { ORGANIZATION_SHORT_FRAGMENT } from '../organizations/graphql'; +import { generateQueryKey, RequestKey, StaleTime } from '../../lib/query'; +import type { LoggedUser } from '../../lib/user'; +import { fetchPricingPreview, PurchaseType } from '../../graphql/paddle'; export const OPPORTUNITY_CONTENT_FRAGMENT = gql` fragment OpportunityContentFragment on OpportunityContentBlock { @@ -125,6 +128,7 @@ export const OPPORTUNITY_FRAGMENT = gql` feedbackQuestions { ...OpportunityFeedbackQuestionFragment } + paid } ${ORGANIZATION_SHORT_FRAGMENT} ${OPPORTUNITY_CONTENT_FRAGMENT} @@ -583,3 +587,24 @@ export const OPPORTUNITY_PREVIEW = gql` } } `; + +export const recruiterPricesQueryOptions = ({ + isLoggedIn, + user, +}: { + isLoggedIn: boolean; + user: LoggedUser; +}) => { + return { + queryKey: generateQueryKey( + RequestKey.PricePreview, + user, + PurchaseType.Recruiter, + ), + queryFn: async () => { + return fetchPricingPreview(PurchaseType.Recruiter); + }, + enabled: isLoggedIn, + staleTime: StaleTime.Default, + }; +}; diff --git a/packages/shared/src/features/opportunity/types.ts b/packages/shared/src/features/opportunity/types.ts index dfb439b65d2..3eab4768793 100644 --- a/packages/shared/src/features/opportunity/types.ts +++ b/packages/shared/src/features/opportunity/types.ts @@ -103,6 +103,7 @@ export type Opportunity = { keywords?: Keyword[]; questions?: OpportunityScreeningQuestion[]; feedbackQuestions?: OpportunityFeedbackQuestion[]; + paid: boolean; }; export type OpportunityMatchDescription = { diff --git a/packages/shared/src/graphql/paddle.ts b/packages/shared/src/graphql/paddle.ts index bd790010a58..f9212f07c3f 100644 --- a/packages/shared/src/graphql/paddle.ts +++ b/packages/shared/src/graphql/paddle.ts @@ -48,6 +48,7 @@ export enum PurchaseType { Plus = 'plus', Organization = 'organization', Cores = 'cores', + Recruiter = 'recruiter', } export enum PlusPlanType { diff --git a/packages/shared/src/hooks/usePaddlePayment.ts b/packages/shared/src/hooks/usePaddlePayment.ts index a19aa3df39c..14a0a054910 100644 --- a/packages/shared/src/hooks/usePaddlePayment.ts +++ b/packages/shared/src/hooks/usePaddlePayment.ts @@ -150,7 +150,7 @@ export const usePaddlePayment = ({ target_type: targetType, event_name: LogEvent.WarningCheckout, extra: JSON.stringify({ - transaction_id: event?.data.transaction_id, + transaction_id: event?.data?.transaction_id, }), }); break; @@ -159,7 +159,7 @@ export const usePaddlePayment = ({ target_type: targetType, event_name: LogEvent.ErrorCheckout, extra: JSON.stringify({ - transaction_id: event?.data.transaction_id, + transaction_id: event?.data?.transaction_id, }), }); break; @@ -168,7 +168,7 @@ export const usePaddlePayment = ({ target_type: targetType, event_name: LogEvent.ErrorPayment, extra: JSON.stringify({ - transaction_id: event?.data.transaction_id, + transaction_id: event?.data?.transaction_id, }), }); break; diff --git a/packages/webapp/pages/recruiter/[opportunityId]/analyze.tsx b/packages/webapp/pages/recruiter/[opportunityId]/analyze.tsx index 640db448b07..78da5431428 100644 --- a/packages/webapp/pages/recruiter/[opportunityId]/analyze.tsx +++ b/packages/webapp/pages/recruiter/[opportunityId]/analyze.tsx @@ -6,9 +6,13 @@ import { useAuthContext } from '@dailydotdev/shared/src/contexts/AuthContext'; import { useLazyModal } from '@dailydotdev/shared/src/hooks/useLazyModal'; import { LazyModal } from '@dailydotdev/shared/src/components/modals/common/types'; import { useRouter } from 'next/router'; -import { OpportunityPreviewProvider } from '@dailydotdev/shared/src/features/opportunity/context/OpportunityPreviewContext'; +import { + OpportunityPreviewProvider, + useOpportunityPreviewContext, +} from '@dailydotdev/shared/src/features/opportunity/context/OpportunityPreviewContext'; import { ContentSidebar } from '@dailydotdev/shared/src/features/opportunity/components/analyze/ContentSidebar'; import { UserTableWrapper } from '@dailydotdev/shared/src/features/opportunity/components/analyze/UserTableWrapper'; +import { webappUrl } from '@dailydotdev/shared/src/lib/constants'; import { getLayout } from '../../../components/layouts/RecruiterSelfServeLayout'; const RecruiterPageContent = () => { @@ -16,6 +20,7 @@ const RecruiterPageContent = () => { const { openModal } = useLazyModal(); const router = useRouter(); const [loadingStep, setLoadingStep] = useState(0); + const { opportunity } = useOpportunityPreviewContext(); useEffect(() => { // Always run the full loading animation sequence @@ -30,14 +35,24 @@ const RecruiterPageContent = () => { }, []); const handlePrepareCampaignClick = useCallback(() => { + if (!opportunity) { + return; + } + + if (!opportunity.paid) { + router.push(`${webappUrl}recruiter/${opportunity.id}/payment`); + + return; + } + if (!user) { openModal({ type: LazyModal.RecruiterSignIn, }); } else { - router.push('/recruiter/prepare'); + router.push(`${webappUrl}recruiter/${opportunity.id}/prepare`); } - }, [user, openModal, router]); + }, [user, openModal, router, opportunity]); return (
diff --git a/packages/webapp/pages/recruiter/payment.tsx b/packages/webapp/pages/recruiter/[opportunityId]/payment.tsx similarity index 90% rename from packages/webapp/pages/recruiter/payment.tsx rename to packages/webapp/pages/recruiter/[opportunityId]/payment.tsx index 0372a44c417..23b61f16420 100644 --- a/packages/webapp/pages/recruiter/payment.tsx +++ b/packages/webapp/pages/recruiter/[opportunityId]/payment.tsx @@ -1,8 +1,7 @@ import type { ReactElement, ReactNode } from 'react'; import React, { useEffect, useRef } from 'react'; import { useRouter } from 'next/router'; -import { usePaymentContext } from '@dailydotdev/shared/src/contexts/payment/context'; -import { PaymentContextProvider } from '@dailydotdev/shared/src/contexts/payment'; +import { RecruiterPaymentContext } from '@dailydotdev/shared/src/contexts/RecruiterPaymentContext/RecruiterPaymentContext'; import HeaderLogo from '@dailydotdev/shared/src/components/layout/HeaderLogo'; import { MoveToIcon } from '@dailydotdev/shared/src/components/icons'; import { @@ -16,23 +15,22 @@ import { TypographyType, } from '@dailydotdev/shared/src/components/typography/Typography'; import { FlexCol } from '@dailydotdev/shared/src/components/utilities'; +import { useRecruiterPaymentContext } from '@dailydotdev/shared/src/contexts/RecruiterPaymentContext/types'; const RecruiterPaymentPage = (): ReactElement => { const router = useRouter(); const checkoutRef = useRef(null); - const { isPaddleReady, openCheckout } = usePaymentContext(); + const { openCheckout, selectedProduct } = useRecruiterPaymentContext(); useEffect(() => { - if (!isPaddleReady) { + if (!selectedProduct) { return; } - // Initialize Paddle checkout with recruiter pricing - // TODO: Replace with actual price ID for recruiter subscription openCheckout({ - priceId: 'your-recruiter-price-id', + priceId: selectedProduct.id, }); - }, [isPaddleReady, openCheckout]); + }, [selectedProduct, openCheckout]); const handleBack = () => { router.back(); @@ -158,7 +156,7 @@ const RecruiterPaymentPage = (): ReactElement => { RecruiterPaymentPage.getLayout = function getLayout( page: ReactNode, ): ReactNode { - return {page}; + return {page}; }; export default RecruiterPaymentPage; From 681587ce004208b795746ce4a3ce287d33afab91 Mon Sep 17 00:00:00 2001 From: capJavert Date: Wed, 10 Dec 2025 16:03:40 +0100 Subject: [PATCH 2/5] fix: types --- packages/shared/src/features/opportunity/mockData.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/shared/src/features/opportunity/mockData.ts b/packages/shared/src/features/opportunity/mockData.ts index 680bdbfc95a..0f1e9b94ef9 100644 --- a/packages/shared/src/features/opportunity/mockData.ts +++ b/packages/shared/src/features/opportunity/mockData.ts @@ -61,6 +61,7 @@ export const mockOpportunity: Opportunity = { { keyword: 'JavaScript' }, { keyword: 'Tailwind CSS' }, ], + paid: false, }; export const mockAnonymousUserTableData: OpportunityPreviewContextType = { From 8fb92ba26a7ddc1e816f2f653d447a492ef45319 Mon Sep 17 00:00:00 2001 From: capJavert Date: Wed, 10 Dec 2025 16:23:46 +0100 Subject: [PATCH 3/5] feat: adjust custom data --- .../contexts/RecruiterPaymentContext/types.ts | 2 +- .../shared/src/contexts/payment/context.ts | 8 +++++--- packages/shared/src/hooks/usePaddlePayment.ts | 6 ++++-- .../recruiter/[opportunityId]/payment.tsx | 18 ++++++++++++++++-- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/packages/shared/src/contexts/RecruiterPaymentContext/types.ts b/packages/shared/src/contexts/RecruiterPaymentContext/types.ts index 8ebc23e8c65..b902c2c4e23 100644 --- a/packages/shared/src/contexts/RecruiterPaymentContext/types.ts +++ b/packages/shared/src/contexts/RecruiterPaymentContext/types.ts @@ -22,7 +22,7 @@ export type ProcessingError = { export type RecruiterPaymentContextData = { paddle?: Paddle | undefined; - openCheckout?: OpenCheckoutFn; + openCheckout?: OpenCheckoutFn<{ opportunity_id: string }>; onCompletion?: () => void; selectedProduct?: RecruiterProductOption; setSelectedProduct: (product: RecruiterProductOption) => void; diff --git a/packages/shared/src/contexts/payment/context.ts b/packages/shared/src/contexts/payment/context.ts index d444c33f415..ffa13aeac25 100644 --- a/packages/shared/src/contexts/payment/context.ts +++ b/packages/shared/src/contexts/payment/context.ts @@ -2,15 +2,17 @@ import type { Dispatch, ReactNode, SetStateAction } from 'react'; import { createContext, useContext } from 'react'; import type { ProductPricingPreview, PurchaseType } from '../../graphql/paddle'; -export interface OpenCheckoutProps { +export interface OpenCheckoutProps> { priceId: string; giftToUserId?: string; - customData?: Record; + customData?: TCustomData; discountId?: string; quantity?: number; } -export type OpenCheckoutFn = (props: OpenCheckoutProps) => void; +export type OpenCheckoutFn> = ( + props: OpenCheckoutProps, +) => void; export interface PaymentContextData { openCheckout?: OpenCheckoutFn; diff --git a/packages/shared/src/hooks/usePaddlePayment.ts b/packages/shared/src/hooks/usePaddlePayment.ts index 14a0a054910..56192c4e908 100644 --- a/packages/shared/src/hooks/usePaddlePayment.ts +++ b/packages/shared/src/hooks/usePaddlePayment.ts @@ -201,12 +201,13 @@ export const usePaddlePayment = ({ }, [router, disabledEvents, targetType, isOrganization, isPlusPlan]); const openCheckout = useCallback( - ({ + ({ priceId, giftToUserId, discountId, quantity = 1, - }: OpenCheckoutProps) => { + customData: customDataProp, + }: OpenCheckoutProps) => { const items: CheckoutLineItem[] = [{ priceId, quantity }]; const customer: CheckoutCustomer = { ...(user?.email @@ -222,6 +223,7 @@ export const usePaddlePayment = ({ }; const customData = { + ...customDataProp, user_id: giftToUserId ?? user?.id, tracking_id: trackingId, ...(!!giftToUserId && { gifter_id: user?.id }), diff --git a/packages/webapp/pages/recruiter/[opportunityId]/payment.tsx b/packages/webapp/pages/recruiter/[opportunityId]/payment.tsx index 23b61f16420..a407b23f8d9 100644 --- a/packages/webapp/pages/recruiter/[opportunityId]/payment.tsx +++ b/packages/webapp/pages/recruiter/[opportunityId]/payment.tsx @@ -16,21 +16,31 @@ import { } from '@dailydotdev/shared/src/components/typography/Typography'; import { FlexCol } from '@dailydotdev/shared/src/components/utilities'; import { useRecruiterPaymentContext } from '@dailydotdev/shared/src/contexts/RecruiterPaymentContext/types'; +import { + OpportunityPreviewProvider, + useOpportunityPreviewContext, +} from '@dailydotdev/shared/src/features/opportunity/context/OpportunityPreviewContext'; const RecruiterPaymentPage = (): ReactElement => { const router = useRouter(); const checkoutRef = useRef(null); const { openCheckout, selectedProduct } = useRecruiterPaymentContext(); + const { opportunity } = useOpportunityPreviewContext(); useEffect(() => { + if (!opportunity) { + return; + } + if (!selectedProduct) { return; } openCheckout({ priceId: selectedProduct.id, + customData: { opportunity_id: opportunity.id }, }); - }, [selectedProduct, openCheckout]); + }, [selectedProduct, openCheckout, opportunity]); const handleBack = () => { router.back(); @@ -156,7 +166,11 @@ const RecruiterPaymentPage = (): ReactElement => { RecruiterPaymentPage.getLayout = function getLayout( page: ReactNode, ): ReactNode { - return {page}; + return ( + + {page} + + ); }; export default RecruiterPaymentPage; From e81cb567e56aac7590d9b31ca71562283074553b Mon Sep 17 00:00:00 2001 From: capJavert Date: Wed, 10 Dec 2025 16:26:35 +0100 Subject: [PATCH 4/5] fix: types --- packages/shared/src/hooks/payment/useStoreKitPayment.ts | 1 + packages/shared/src/lib/log.ts | 2 ++ 2 files changed, 3 insertions(+) diff --git a/packages/shared/src/hooks/payment/useStoreKitPayment.ts b/packages/shared/src/hooks/payment/useStoreKitPayment.ts index 5f1f61f6d67..8aa89e246a2 100644 --- a/packages/shared/src/hooks/payment/useStoreKitPayment.ts +++ b/packages/shared/src/hooks/payment/useStoreKitPayment.ts @@ -22,6 +22,7 @@ const typeToHandler: Record = { cores: WebKitMessageHandlers.IAPCoresPurchase, plus: WebKitMessageHandlers.IAPSubscriptionRequest, organization: WebKitMessageHandlers.IAPSubscriptionRequest, + recruiter: WebKitMessageHandlers.IAPSubscriptionRequest, // not used for now on apple side }; export const useStoreKitPayment = ({ diff --git a/packages/shared/src/lib/log.ts b/packages/shared/src/lib/log.ts index 13e8d3dc813..dc539710d29 100644 --- a/packages/shared/src/lib/log.ts +++ b/packages/shared/src/lib/log.ts @@ -391,6 +391,7 @@ export enum TargetType { // CV CvBanner = 'cv banner', Post = 'post', + Recruiter = 'recruiter', } export enum TargetId { @@ -501,4 +502,5 @@ export const purchaseTypeToTargetType: Record = { cores: TargetType.Credits, plus: TargetType.Plus, organization: TargetType.Plus, + recruiter: TargetType.Recruiter, }; From d5fe265bd1bdb7c2d57c3cb9bff37ba9b644a514 Mon Sep 17 00:00:00 2001 From: capJavert Date: Thu, 11 Dec 2025 10:18:27 +0100 Subject: [PATCH 5/5] refactor: paid to subscriptionStatus --- packages/shared/src/features/opportunity/graphql.ts | 2 +- packages/shared/src/features/opportunity/mockData.ts | 3 ++- packages/shared/src/features/opportunity/types.ts | 3 ++- packages/shared/src/lib/plus.ts | 1 + packages/webapp/pages/recruiter/[opportunityId]/analyze.tsx | 3 ++- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/shared/src/features/opportunity/graphql.ts b/packages/shared/src/features/opportunity/graphql.ts index 7d2efd606ac..09ece7c08a9 100644 --- a/packages/shared/src/features/opportunity/graphql.ts +++ b/packages/shared/src/features/opportunity/graphql.ts @@ -128,7 +128,7 @@ export const OPPORTUNITY_FRAGMENT = gql` feedbackQuestions { ...OpportunityFeedbackQuestionFragment } - paid + subscriptionStatus } ${ORGANIZATION_SHORT_FRAGMENT} ${OPPORTUNITY_CONTENT_FRAGMENT} diff --git a/packages/shared/src/features/opportunity/mockData.ts b/packages/shared/src/features/opportunity/mockData.ts index 0f1e9b94ef9..758b6d447bd 100644 --- a/packages/shared/src/features/opportunity/mockData.ts +++ b/packages/shared/src/features/opportunity/mockData.ts @@ -2,6 +2,7 @@ import type { Opportunity } from './types'; import type { OpportunityPreviewContextType } from './context/OpportunityPreviewContext'; import { SeniorityLevel } from './protobuf/opportunity'; import { SourceMemberRole, SourceType } from '../../graphql/sources'; +import { SubscriptionStatus } from '../../lib/plus'; export const mockOpportunity: Opportunity = { id: '89f3daff-d6bb-4652-8f9c-b9f7254c9af1', @@ -61,7 +62,7 @@ export const mockOpportunity: Opportunity = { { keyword: 'JavaScript' }, { keyword: 'Tailwind CSS' }, ], - paid: false, + subscriptionStatus: SubscriptionStatus.None, }; export const mockAnonymousUserTableData: OpportunityPreviewContextType = { diff --git a/packages/shared/src/features/opportunity/types.ts b/packages/shared/src/features/opportunity/types.ts index 3eab4768793..24657085c34 100644 --- a/packages/shared/src/features/opportunity/types.ts +++ b/packages/shared/src/features/opportunity/types.ts @@ -12,6 +12,7 @@ import type { LocationType } from './protobuf/util'; import type { Connection } from '../../graphql/common'; import type { Squad } from '../../graphql/sources'; import type { TopReader } from '../../components/badges/TopReaderBadge'; +import type { SubscriptionStatus } from '../../lib/plus'; export enum OpportunityMatchStatus { Pending = 'pending', @@ -103,7 +104,7 @@ export type Opportunity = { keywords?: Keyword[]; questions?: OpportunityScreeningQuestion[]; feedbackQuestions?: OpportunityFeedbackQuestion[]; - paid: boolean; + subscriptionStatus: SubscriptionStatus; }; export type OpportunityMatchDescription = { diff --git a/packages/shared/src/lib/plus.ts b/packages/shared/src/lib/plus.ts index 89d9195f6b7..640df0a7ad8 100644 --- a/packages/shared/src/lib/plus.ts +++ b/packages/shared/src/lib/plus.ts @@ -7,4 +7,5 @@ export enum SubscriptionStatus { Active = 'active', Expired = 'expired', Cancelled = 'cancelled', + None = 'none', } diff --git a/packages/webapp/pages/recruiter/[opportunityId]/analyze.tsx b/packages/webapp/pages/recruiter/[opportunityId]/analyze.tsx index 78da5431428..4d3b272eea5 100644 --- a/packages/webapp/pages/recruiter/[opportunityId]/analyze.tsx +++ b/packages/webapp/pages/recruiter/[opportunityId]/analyze.tsx @@ -13,6 +13,7 @@ import { import { ContentSidebar } from '@dailydotdev/shared/src/features/opportunity/components/analyze/ContentSidebar'; import { UserTableWrapper } from '@dailydotdev/shared/src/features/opportunity/components/analyze/UserTableWrapper'; import { webappUrl } from '@dailydotdev/shared/src/lib/constants'; +import { SubscriptionStatus } from '@dailydotdev/shared/src/lib/plus'; import { getLayout } from '../../../components/layouts/RecruiterSelfServeLayout'; const RecruiterPageContent = () => { @@ -39,7 +40,7 @@ const RecruiterPageContent = () => { return; } - if (!opportunity.paid) { + if (opportunity.subscriptionStatus !== SubscriptionStatus.Active) { router.push(`${webappUrl}recruiter/${opportunity.id}/payment`); return;