diff --git a/packages/components/src/registration/onboarding/DiscoveryDropdown.tsx b/packages/components/src/registration/onboarding/DiscoveryDropdown.tsx
deleted file mode 100644
index a2c4fd9..0000000
--- a/packages/components/src/registration/onboarding/DiscoveryDropdown.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import { Controller, useFormContext } from "react-hook-form";
-
-import {
- Select,
- SelectContent,
- SelectGroup,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@good-dog/ui/select";
-
-export default function DiscoveryDropdown() {
- const { control } = useFormContext<{
- discovery?: string;
- }>();
-
- return (
-
-
How did you hear about Good Dog?
- (
-
- )}
- />
-
- );
-}
diff --git a/packages/components/src/registration/onboarding/MediaMakerForm.tsx b/packages/components/src/registration/onboarding/MediaMakerForm.tsx
index 9d75057..e2f3223 100644
--- a/packages/components/src/registration/onboarding/MediaMakerForm.tsx
+++ b/packages/components/src/registration/onboarding/MediaMakerForm.tsx
@@ -5,14 +5,20 @@ import { z } from "zod";
import { zPreProcessEmptyString } from "@good-dog/trpc/utils";
import RegistrationInput from "../inputs/RegistrationInput";
-import DiscoveryDropdown from "./DiscoveryDropdown";
import OnboardingFormProvider from "./OnboardingFormProvider";
+import ReferralDropdown from "./ReferralDropdown";
+import { ReferralSource } from ".prisma/client";
const Schema = z.object({
role: z.literal("MEDIA_MAKER"),
firstName: zPreProcessEmptyString(z.string()),
lastName: zPreProcessEmptyString(z.string()),
- discovery: z.string().optional(),
+ referral: z
+ .object({
+ source: z.nativeEnum(ReferralSource),
+ customSource: zPreProcessEmptyString(z.string().optional()),
+ })
+ .optional(),
});
type FormValues = z.infer;
@@ -51,7 +57,7 @@ export default function MediaMakerForm(
label="Last Name"
/>
-
+
);
}
diff --git a/packages/components/src/registration/onboarding/MusicianForm.tsx b/packages/components/src/registration/onboarding/MusicianForm.tsx
index fae6df9..2a52409 100644
--- a/packages/components/src/registration/onboarding/MusicianForm.tsx
+++ b/packages/components/src/registration/onboarding/MusicianForm.tsx
@@ -3,13 +3,14 @@
import { useFieldArray, useFormContext } from "react-hook-form";
import { z } from "zod";
+import { ReferralSource } from "@good-dog/db";
import { zPreProcessEmptyString } from "@good-dog/trpc/utils";
import { Button } from "@good-dog/ui/button";
import RegistrationCheckbox from "../inputs/RegistrationCheckbox";
import RegistrationInput from "../inputs/RegistrationInput";
-import DiscoveryDropdown from "./DiscoveryDropdown";
import OnboardingFormProvider from "./OnboardingFormProvider";
+import ReferralDropdown from "./ReferralDropdown";
const Schema = z.object({
role: z.literal("MUSICIAN"),
@@ -33,7 +34,12 @@ const Schema = z.object({
}),
)
.optional(),
- discovery: z.string().optional(),
+ referral: z
+ .object({
+ source: z.nativeEnum(ReferralSource),
+ customSource: zPreProcessEmptyString(z.string().optional()),
+ })
+ .optional(),
});
type FormValues = z.infer;
@@ -103,7 +109,7 @@ export default function MusicianForm(
-
+
);
}
diff --git a/packages/components/src/registration/onboarding/OnboardingFormProvider.tsx b/packages/components/src/registration/onboarding/OnboardingFormProvider.tsx
index 71286c5..e0c7734 100644
--- a/packages/components/src/registration/onboarding/OnboardingFormProvider.tsx
+++ b/packages/components/src/registration/onboarding/OnboardingFormProvider.tsx
@@ -7,6 +7,7 @@ import { useRouter } from "next/navigation";
import { zodResolver } from "@hookform/resolvers/zod";
import { FormProvider, useForm } from "react-hook-form";
+import type { ReferralType } from "@good-dog/db";
import { trpc } from "@good-dog/trpc/client";
import { Button } from "@good-dog/ui/button";
@@ -14,7 +15,10 @@ interface BaseValues {
role: "MEDIA_MAKER" | "MUSICIAN";
firstName: string;
lastName: string;
- discovery?: string;
+ referral?: {
+ source: ReferralType;
+ customSource?: string;
+ };
}
export default function OnboardingFormProvider<
diff --git a/packages/components/src/registration/onboarding/ReferralDropdown.tsx b/packages/components/src/registration/onboarding/ReferralDropdown.tsx
new file mode 100644
index 0000000..add965e
--- /dev/null
+++ b/packages/components/src/registration/onboarding/ReferralDropdown.tsx
@@ -0,0 +1,74 @@
+import { useState } from "react";
+import { Controller, useFormContext } from "react-hook-form";
+
+import { ReferralSource } from "@good-dog/db";
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@good-dog/ui/select";
+
+export default function ReferralDropdown() {
+ const { control, register } = useFormContext<{
+ source?: string;
+ customSource?: string;
+ }>();
+
+ const referralOptions = Object.values(ReferralSource);
+
+ const [isOtherSelected, setIsOtherSelected] = useState(false);
+
+ const handleSelectChange = (value: string) => {
+ setIsOtherSelected(value === "OTHER");
+ };
+
+ return (
+
+
How did you hear about Good Dog?
+
(
+
+ )}
+ />
+
+ {isOtherSelected && (
+
+
+
+
+ )}
+
+ );
+}
diff --git a/packages/db/prisma/migrations/20250119220136_added_referral_source/migration.sql b/packages/db/prisma/migrations/20250119220136_added_referral_source/migration.sql
new file mode 100644
index 0000000..8741d8a
--- /dev/null
+++ b/packages/db/prisma/migrations/20250119220136_added_referral_source/migration.sql
@@ -0,0 +1,20 @@
+-- CreateEnum
+CREATE TYPE "ReferralSource" AS ENUM ('FRIEND', 'COLLEAGUE', 'GREEN_LINE_RECORDS', 'SOCIAL_MEDIA', 'OTHER');
+
+-- CreateTable
+CREATE TABLE "Referral" (
+ "id" TEXT NOT NULL,
+ "userId" TEXT NOT NULL,
+ "source" "ReferralSource" NOT NULL,
+ "customSource" TEXT,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL,
+
+ CONSTRAINT "Referral_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateIndex
+CREATE UNIQUE INDEX "Referral_userId_key" ON "Referral"("userId");
+
+-- AddForeignKey
+ALTER TABLE "Referral" ADD CONSTRAINT "Referral_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma
index 1d39213..b1bb749 100644
--- a/packages/db/prisma/schema.prisma
+++ b/packages/db/prisma/schema.prisma
@@ -37,10 +37,29 @@ model User {
passwordResetReq PasswordResetReq?
sessions Session[]
sentInvites GroupInvite[]
+ referral Referral?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
+model Referral {
+ id String @id @default(uuid())
+ user User @relation(fields: [userId], references: [userId])
+ userId String @unique
+ source ReferralSource
+ customSource String?
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+}
+
+enum ReferralSource {
+ FRIEND
+ COLLEAGUE
+ GREEN_LINE_RECORDS
+ SOCIAL_MEDIA
+ OTHER
+}
+
model Session {
sessionId String @id @default(uuid()) @map("id")
userId String
diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts
index 901f3a0..6148355 100644
--- a/packages/db/src/index.ts
+++ b/packages/db/src/index.ts
@@ -1,3 +1,6 @@
-import { PrismaClient } from "@prisma/client";
+import { PrismaClient, ReferralSource } from "@prisma/client";
export const prisma = new PrismaClient();
+
+export { ReferralSource };
+export type ReferralType = keyof typeof ReferralSource;
diff --git a/packages/trpc/src/procedures/onboarding.ts b/packages/trpc/src/procedures/onboarding.ts
index bbd3bdd..15a56b3 100644
--- a/packages/trpc/src/procedures/onboarding.ts
+++ b/packages/trpc/src/procedures/onboarding.ts
@@ -2,6 +2,8 @@ import { revalidatePath } from "next/cache";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
+import { ReferralSource } from "@good-dog/db";
+
import { authenticatedProcedureBuilder } from "../internal/init";
import { zPreProcessEmptyString } from "../utils";
@@ -12,12 +14,23 @@ export const onboardingProcedure = authenticatedProcedureBuilder
role: z.literal("MEDIA_MAKER"),
firstName: z.string(),
lastName: z.string(),
- discovery: z.string().optional(),
+ referral: z
+ .object({
+ source: z.nativeEnum(ReferralSource),
+ customSource: zPreProcessEmptyString(z.string().optional()),
+ })
+ .optional(),
}),
z.object({
role: z.literal("MUSICIAN"),
firstName: zPreProcessEmptyString(z.string()),
lastName: zPreProcessEmptyString(z.string()),
+ referral: z
+ .object({
+ source: z.nativeEnum(ReferralSource),
+ customSource: zPreProcessEmptyString(z.string().optional()),
+ })
+ .optional(),
groupName: zPreProcessEmptyString(z.string()),
stageName: zPreProcessEmptyString(z.string().optional()),
isSongWriter: z.boolean().optional(),
@@ -36,7 +49,6 @@ export const onboardingProcedure = authenticatedProcedureBuilder
}),
)
.optional(),
- discovery: z.string().optional(),
}),
]),
)
@@ -66,6 +78,14 @@ export const onboardingProcedure = authenticatedProcedureBuilder
role: "MUSICIAN",
firstName: input.firstName,
lastName: input.lastName,
+ referral: input.referral
+ ? {
+ create: {
+ source: input.referral.source,
+ customSource: input.referral.customSource,
+ },
+ }
+ : undefined,
stageName: input.stageName,
isSongWriter: input.isSongWriter,
isAscapAffiliated: input.isAscapAffiliated,