Skip to content

Commit 5af2387

Browse files
authored
Merge pull request #698 from Merit-Systems/br/add-echo-referral-handler
add referral handler
2 parents 293fa6f + df12300 commit 5af2387

4 files changed

Lines changed: 65 additions & 1 deletion

File tree

packages/app/control/src/app/(app)/app/[id]/(overview)/_components/header/buttons/index.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ interface Props {
1212
}
1313

1414
export const HeaderButtons: React.FC<Props> = ({ appId }) => {
15-
const [isOwner] = api.apps.app.isOwner.useSuspenseQuery(appId);
15+
const { data: isOwner, isLoading } = api.apps.app.isOwner.useQuery(appId);
16+
17+
if (isLoading || isOwner === undefined) {
18+
return <LoadingHeaderButtons />;
19+
}
1620

1721
return (
1822
<div className="flex items-center gap-2">
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use client';
2+
3+
import { useEffect, useState } from 'react';
4+
import { useSearchParams } from 'next/navigation';
5+
import { api } from '@/trpc/client';
6+
7+
interface Props {
8+
appId: string;
9+
}
10+
11+
export const ReferralHandler: React.FC<Props> = ({ appId }) => {
12+
const searchParams = useSearchParams();
13+
const referralCode = searchParams.get('referral_code');
14+
const [processed, setProcessed] = useState(false);
15+
16+
const { mutateAsync: registerReferral } =
17+
api.apps.app.registerReferral.useMutation();
18+
19+
useEffect(() => {
20+
if (!referralCode || processed) return;
21+
22+
const processReferralCode = async () => {
23+
await registerReferral({
24+
appId,
25+
code: referralCode,
26+
}).catch(() => {
27+
// Silently fail - referral code may be invalid, expired, or user may already have a referrer
28+
});
29+
30+
setProcessed(true);
31+
};
32+
33+
void processReferralCode();
34+
}, [referralCode, appId, registerReferral, processed]);
35+
36+
return null;
37+
};

packages/app/control/src/app/(app)/app/[id]/(overview)/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { api, HydrateClient } from '@/trpc/server';
99
import { HeaderCard, LoadingHeaderCard } from './_components/header';
1010
import { Setup } from './_components/setup';
1111
import { Overview } from './_components/overview';
12+
import { ReferralHandler } from './_components/referral-handler';
1213
import { userOrRedirect } from '@/auth/user-or-redirect';
1314

1415
export default async function AppPage(props: PageProps<'/app/[id]'>) {
@@ -26,6 +27,7 @@ export default async function AppPage(props: PageProps<'/app/[id]'>) {
2627

2728
return (
2829
<HydrateClient>
30+
<ReferralHandler appId={id} />
2931
<Body className="gap-0 pt-0">
3032
<Suspense fallback={<LoadingHeaderCard />}>
3133
<HeaderCard appId={id} />

packages/app/control/src/trpc/routers/apps/index.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
createAppMembershipSchema,
2424
updateAppMembershipReferrer,
2525
updateAppMembershipReferrerSchema,
26+
setAppMembershipReferrer,
2627
} from '@/services/db/apps/membership';
2728
import {
2829
listAppsSchema,
@@ -262,6 +263,26 @@ export const appsRouter = createTRPCRouter({
262263
}),
263264
},
264265

266+
registerReferral: protectedProcedure
267+
.input(z.object({ appId: appIdSchema, code: z.string() }))
268+
.mutation(async ({ input, ctx }) => {
269+
const success = await setAppMembershipReferrer(
270+
ctx.session.user.id,
271+
input.appId,
272+
input.code
273+
);
274+
275+
if (!success) {
276+
throw new TRPCError({
277+
code: 'BAD_REQUEST',
278+
message:
279+
'Referral code could not be applied. It may be invalid, expired, or you may already have a referrer for this app.',
280+
});
281+
}
282+
283+
return { success: true };
284+
}),
285+
265286
transactions: {
266287
list: paginatedProcedure
267288
.concat(protectedProcedure)

0 commit comments

Comments
 (0)