From 663fa0124f6e113ca2cc8f815b27738c9d5290a3 Mon Sep 17 00:00:00 2001 From: Dhruwang Jariwala <67850763+Dhruwang@users.noreply.github.com> Date: Tue, 12 Dec 2023 16:02:51 +0530 Subject: [PATCH] chore: renamed profile to user (#1770) Co-authored-by: Matthias Nannt --- .../[personId]/components/ResponseSection.tsx | 2 +- .../components/ResponseTimeline.tsx | 8 +- .../[personId]/components/ResponsesFeed.tsx | 8 +- .../settings/notifications/actions.ts | 2 +- .../components/NotificationSwitch.tsx | 2 +- .../settings/notifications/page.tsx | 2 +- .../settings/notifications/types.ts | 2 +- .../settings/profile/actions.ts | 14 +-- .../profile/components/AccountSecurity.tsx | 6 +- .../profile/components/DeleteAccount.tsx | 4 +- .../settings/profile/components/EditName.tsx | 16 +-- .../[environmentId]/settings/profile/page.tsx | 14 +-- .../responses/components/ResponsePage.tsx | 10 +- .../responses/components/ResponseTimeline.tsx | 8 +- .../[surveyId]/(analysis)/responses/page.tsx | 10 +- .../summary/components/LinkModalButton.tsx | 8 +- .../summary/components/ShareEmbedSurvey.tsx | 8 +- .../summary/components/SuccessMessage.tsx | 8 +- .../summary/components/SummaryPage.tsx | 8 +- .../[surveyId]/(analysis)/summary/page.tsx | 10 +- .../[surveyId]/components/SummaryHeader.tsx | 12 +-- .../surveys/components/SurveyList.tsx | 2 +- .../surveys/components/SurveyStarter.tsx | 8 +- .../surveys/templates/TemplateContainer.tsx | 8 +- .../surveys/templates/TemplateList.tsx | 16 +-- .../surveys/templates/page.tsx | 2 +- apps/web/app/(app)/onboarding/actions.ts | 8 +- .../(app)/onboarding/components/Objective.tsx | 14 +-- .../onboarding/components/Onboarding.tsx | 21 ++-- .../app/(app)/onboarding/components/Role.tsx | 4 +- apps/web/app/(app)/onboarding/page.tsx | 10 +- apps/web/app/api/cron/weekly_summary/types.ts | 2 +- apps/web/app/api/pipeline/route.ts | 2 +- apps/web/app/api/v1/users/route.ts | 4 +- apps/web/app/lib/users/users.ts | 2 +- packages/database/jsonTypes.ts | 2 +- packages/database/zod-utils.ts | 2 +- packages/ee/RoleManagement/lib/actions.ts | 14 +-- packages/lib/auth/service.ts | 6 +- packages/lib/authOptions.ts | 14 +-- packages/lib/membership/hooks/actions.ts | 4 +- packages/lib/profile/util.ts | 15 --- packages/lib/{profile => user}/cache.ts | 6 +- packages/lib/{profile => user}/service.ts | 102 ++++++++---------- packages/types/auth.ts | 4 +- packages/types/next-auth.d.ts | 4 +- packages/types/templates.ts | 4 +- packages/types/{profile.ts => user.ts} | 29 +++-- packages/types/users.ts | 8 -- .../components/ResponseNote.tsx | 10 +- packages/ui/SingleResponseCard/index.tsx | 8 +- 51 files changed, 234 insertions(+), 263 deletions(-) delete mode 100644 packages/lib/profile/util.ts rename packages/lib/{profile => user}/cache.ts (80%) rename packages/lib/{profile => user}/service.ts (64%) rename packages/types/{profile.ts => user.ts} (61%) delete mode 100644 packages/types/users.ts diff --git a/apps/web/app/(app)/environments/[environmentId]/people/[personId]/components/ResponseSection.tsx b/apps/web/app/(app)/environments/[environmentId]/people/[personId]/components/ResponseSection.tsx index 803b775662..326a95b3fa 100644 --- a/apps/web/app/(app)/environments/[environmentId]/people/[personId]/components/ResponseSection.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/people/[personId]/components/ResponseSection.tsx @@ -29,7 +29,7 @@ export default async function ResponseSection({ <> {responses && ( diff --git a/apps/web/app/(app)/environments/[environmentId]/people/[personId]/components/ResponsesFeed.tsx b/apps/web/app/(app)/environments/[environmentId]/people/[personId]/components/ResponsesFeed.tsx index 50d1fddb99..978c7cf624 100644 --- a/apps/web/app/(app)/environments/[environmentId]/people/[personId]/components/ResponsesFeed.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/people/[personId]/components/ResponsesFeed.tsx @@ -1,6 +1,6 @@ import EmptySpaceFiller from "@formbricks/ui/EmptySpaceFiller"; import { TEnvironment } from "@formbricks/types/environment"; -import { TProfile } from "@formbricks/types/profile"; +import { TUser } from "@formbricks/types/user"; import { TResponse } from "@formbricks/types/responses"; import { TSurvey } from "@formbricks/types/surveys"; import { TTag } from "@formbricks/types/tags"; @@ -10,13 +10,13 @@ export default async function ResponseFeed({ responses, environment, surveys, - profile, + user, environmentTags, }: { responses: TResponse[]; environment: TEnvironment; surveys: TSurvey[]; - profile: TProfile; + user: TUser; environmentTags: TTag[]; }) { return ( @@ -34,7 +34,7 @@ export default async function ResponseFeed({ ) { +export async function updateUserAction(data: Partial) { const session = await getServerSession(authOptions); if (!session) throw new AuthorizationError("Not authorized"); - return await updateProfile(session.user.id, data); + return await updateUser(session.user.id, data); } -export async function deleteProfileAction() { +export async function deleteUserAction() { const session = await getServerSession(authOptions); if (!session) throw new AuthorizationError("Not authorized"); - return await deleteProfile(session.user.id); + return await deleteUser(session.user.id); } export async function setupTwoFactorAuthAction(password: string) { @@ -79,5 +79,5 @@ export async function updateAvatarAction(avatarUrl: string) { throw new Error("User not found"); } - return await updateProfile(session.user.id, { imageUrl: avatarUrl }); + return await updateUser(session.user.id, { imageUrl: avatarUrl }); } diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/profile/components/AccountSecurity.tsx b/apps/web/app/(app)/environments/[environmentId]/settings/profile/components/AccountSecurity.tsx index 8eb1201dc8..93874e0747 100644 --- a/apps/web/app/(app)/environments/[environmentId]/settings/profile/components/AccountSecurity.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/settings/profile/components/AccountSecurity.tsx @@ -2,11 +2,11 @@ import DisableTwoFactorModal from "@/app/(app)/environments/[environmentId]/settings/profile/components/DisableTwoFactorModal"; import EnableTwoFactorModal from "@/app/(app)/environments/[environmentId]/settings/profile/components/EnableTwoFactorModal"; -import { TProfile } from "@formbricks/types/profile"; +import { TUser } from "@formbricks/types/user"; import { Switch } from "@formbricks/ui/Switch"; import React, { useState } from "react"; -const AccountSecurity = ({ profile }: { profile: TProfile }) => { +const AccountSecurity = ({ user }: { user: TUser }) => { const [twoFactorModalOpen, setTwoFactorModalOpen] = useState(false); const [disableTwoFactorModalOpen, setDisableTwoFactorModalOpen] = useState(false); @@ -14,7 +14,7 @@ const AccountSecurity = ({ profile }: { profile: TProfile }) => {
{ if (checked) { setTwoFactorModalOpen(true); diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/profile/components/DeleteAccount.tsx b/apps/web/app/(app)/environments/[environmentId]/settings/profile/components/DeleteAccount.tsx index da850d4c08..5ef39b8f88 100644 --- a/apps/web/app/(app)/environments/[environmentId]/settings/profile/components/DeleteAccount.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/settings/profile/components/DeleteAccount.tsx @@ -11,7 +11,7 @@ import { signOut } from "next-auth/react"; import Image from "next/image"; import { Dispatch, SetStateAction, useState } from "react"; import toast from "react-hot-toast"; -import { deleteProfileAction } from "../actions"; +import { deleteUserAction } from "../actions"; export function EditAvatar({ session }) { return ( @@ -52,7 +52,7 @@ function DeleteAccountModal({ setOpen, open, session }: DeleteAccountModalProps) const deleteAccount = async () => { try { setDeleting(true); - await deleteProfileAction(); + await deleteUserAction(); await signOut(); await formbricksLogout(); } catch (error) { diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/profile/components/EditName.tsx b/apps/web/app/(app)/environments/[environmentId]/settings/profile/components/EditName.tsx index 77be58026d..43999bb0f2 100644 --- a/apps/web/app/(app)/environments/[environmentId]/settings/profile/components/EditName.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/settings/profile/components/EditName.tsx @@ -5,14 +5,14 @@ import { Input } from "@formbricks/ui/Input"; import { Label } from "@formbricks/ui/Label"; import { useForm, SubmitHandler } from "react-hook-form"; import toast from "react-hot-toast"; -import { updateProfileAction } from "../actions"; -import { TProfile } from "@formbricks/types/profile"; +import { updateUserAction } from "../actions"; +import { TUser } from "@formbricks/types/user"; type FormData = { name: string; }; -export function EditName({ profile }: { profile: TProfile }) { +export function EditName({ user }: { user: TUser }) { const { register, handleSubmit, @@ -20,7 +20,7 @@ export function EditName({ profile }: { profile: TProfile }) { watch, } = useForm(); - const nameValue = watch("name", profile.name || ""); + const nameValue = watch("name", user.name || ""); const isNotEmptySpaces = (value: string) => value.trim() !== ""; const onSubmit: SubmitHandler = async (data) => { @@ -30,11 +30,11 @@ export function EditName({ profile }: { profile: TProfile }) { toast.error("Please enter at least one character"); return; } - if (data.name === profile.name) { + if (data.name === user.name) { toast.success("This is already your name"); return; } - await updateProfileAction({ name: data.name }); + await updateUserAction({ name: data.name }); toast.success("Your name was updated successfully"); } catch (error) { toast.error(`Error: ${error.message}`); @@ -48,13 +48,13 @@ export function EditName({ profile }: { profile: TProfile }) {
- +
{survey.type === "link" && ( - + )} {!isViewer && (environment?.widgetSetupCompleted || survey.type === "link") && @@ -92,7 +92,7 @@ const SummaryHeader = ({ survey={survey} webAppUrl={webAppUrl} product={product} - profile={profile} + user={user} /> @@ -177,7 +177,7 @@ const SummaryHeader = ({ survey={survey} webAppUrl={webAppUrl} product={product} - profile={profile} + user={user} />
); diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/components/SurveyList.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/components/SurveyList.tsx index 95017fd1dd..871ae1c198 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/components/SurveyList.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/components/SurveyList.tsx @@ -53,7 +53,7 @@ export default async function SurveysList({ environmentId }: { environmentId: st environmentId={environmentId} environment={environment} product={product} - profile={session.user} + user={session.user} /> ); } diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/components/SurveyStarter.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/components/SurveyStarter.tsx index e068a1f7a6..7c86638b2a 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/components/SurveyStarter.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/components/SurveyStarter.tsx @@ -9,18 +9,18 @@ import { TTemplate } from "@formbricks/types/templates"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { toast } from "react-hot-toast"; -import { TProfile } from "@formbricks/types/profile"; +import { TUser } from "@formbricks/types/user"; export default function SurveyStarter({ environmentId, environment, product, - profile, + user, }: { environmentId: string; environment: TEnvironment; product: TProduct; - profile: TProfile; + user: TUser; }) { const [isCreateSurveyLoading, setIsCreateSurveyLoading] = useState(false); const router = useRouter(); @@ -59,7 +59,7 @@ export default function SurveyStarter({ }} environment={environment} product={product} - profile={profile} + user={user} /> )} diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/templates/TemplateContainer.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/templates/TemplateContainer.tsx index 30b9bcd15c..11d76d6f38 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/templates/TemplateContainer.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/templates/TemplateContainer.tsx @@ -10,20 +10,20 @@ import TemplateList from "./TemplateList"; import type { TProduct } from "@formbricks/types/product"; import type { TEnvironment } from "@formbricks/types/environment"; import { SearchBox } from "@formbricks/ui/SearchBox"; -import { TProfile } from "@formbricks/types/profile"; +import { TUser } from "@formbricks/types/user"; type TemplateContainerWithPreviewProps = { environmentId: string; product: TProduct; environment: TEnvironment; - profile: TProfile; + user: TUser; }; export default function TemplateContainerWithPreview({ environmentId, product, environment, - profile, + user, }: TemplateContainerWithPreviewProps) { const [activeTemplate, setActiveTemplate] = useState(null); const [activeQuestionId, setActiveQuestionId] = useState(null); @@ -59,7 +59,7 @@ export default function TemplateContainerWithPreview({ environmentId={environmentId} environment={environment} product={product} - profile={profile} + user={user} templateSearch={templateSearch ?? ""} onTemplateClick={(template) => { setActiveQuestionId(template.preset.questions[0].id); diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/templates/TemplateList.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/templates/TemplateList.tsx index a34fdd0eac..71f954af83 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/templates/TemplateList.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/templates/TemplateList.tsx @@ -4,7 +4,7 @@ import { replacePresetPlaceholders } from "@/app/lib/templates"; import { cn } from "@formbricks/lib/cn"; import type { TEnvironment } from "@formbricks/types/environment"; import type { TProduct } from "@formbricks/types/product"; -import { TProfile } from "@formbricks/types/profile"; +import { TUser } from "@formbricks/types/user"; import { TSurveyInput } from "@formbricks/types/surveys"; import { TTemplate } from "@formbricks/types/templates"; import { Button } from "@formbricks/ui/Button"; @@ -19,7 +19,7 @@ import { customSurvey, templates, testTemplate } from "./templates"; type TemplateList = { environmentId: string; - profile: TProfile; + user: TUser; onTemplateClick: (template: TTemplate) => void; environment: TEnvironment; product: TProduct; @@ -30,7 +30,7 @@ const ALL_CATEGORY_NAME = "All"; const RECOMMENDED_CATEGORY_NAME = "For you"; export default function TemplateList({ environmentId, - profile, + user, onTemplateClick, product, environment, @@ -50,7 +50,7 @@ export default function TemplateList({ ]; const fullCategories = - !!profile?.objective && profile.objective !== "other" + !!user?.objective && user.objective !== "other" ? [RECOMMENDED_CATEGORY_NAME, ALL_CATEGORY_NAME, ...defaultCategories] : [ALL_CATEGORY_NAME, ...defaultCategories]; @@ -58,11 +58,11 @@ export default function TemplateList({ const activeFilter = templateSearch ? ALL_CATEGORY_NAME - : !!profile?.objective && profile.objective !== "other" + : !!user?.objective && user.objective !== "other" ? RECOMMENDED_CATEGORY_NAME : ALL_CATEGORY_NAME; setSelectedFilter(activeFilter); - }, [profile, templateSearch]); + }, [user, templateSearch]); const addSurvey = async (activeTemplate) => { setLoading(true); @@ -81,9 +81,9 @@ export default function TemplateList({ const matchesCategory = selectedFilter === ALL_CATEGORY_NAME || template.category === selectedFilter || - (profile.objective && + (user.objective && selectedFilter === RECOMMENDED_CATEGORY_NAME && - template.objectives?.includes(profile.objective)); + template.objectives?.includes(user.objective)); const templateName = template.name?.toLowerCase(); const templateDescription = template.description?.toLowerCase(); diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/templates/page.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/templates/page.tsx index 8900194c3b..f4d037012f 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/templates/page.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/templates/page.tsx @@ -28,7 +28,7 @@ export default async function SurveyTemplatesPage({ params }) { return ( diff --git a/apps/web/app/(app)/onboarding/actions.ts b/apps/web/app/(app)/onboarding/actions.ts index 449bb5c7ad..895c03312a 100644 --- a/apps/web/app/(app)/onboarding/actions.ts +++ b/apps/web/app/(app)/onboarding/actions.ts @@ -3,17 +3,17 @@ import { authOptions } from "@formbricks/lib/authOptions"; import { canUserAccessProduct, verifyUserRoleAccess } from "@formbricks/lib/product/auth"; import { getProduct, updateProduct } from "@formbricks/lib/product/service"; -import { updateProfile } from "@formbricks/lib/profile/service"; +import { updateUser } from "@formbricks/lib/user/service"; import { AuthorizationError } from "@formbricks/types/errors"; import { TProductUpdateInput } from "@formbricks/types/product"; -import { TProfileUpdateInput } from "@formbricks/types/profile"; +import { TUserUpdateInput } from "@formbricks/types/user"; import { getServerSession } from "next-auth"; -export async function updateProfileAction(updatedProfile: TProfileUpdateInput) { +export async function updateUserAction(updatedUser: TUserUpdateInput) { const session = await getServerSession(authOptions); if (!session) throw new AuthorizationError("Not authorized"); - return await updateProfile(session.user.id, updatedProfile); + return await updateUser(session.user.id, updatedUser); } export async function updateProductAction(productId: string, updatedProduct: Partial) { diff --git a/apps/web/app/(app)/onboarding/components/Objective.tsx b/apps/web/app/(app)/onboarding/components/Objective.tsx index a15dcc6fb2..ed9a4efba2 100644 --- a/apps/web/app/(app)/onboarding/components/Objective.tsx +++ b/apps/web/app/(app)/onboarding/components/Objective.tsx @@ -1,10 +1,10 @@ "use client"; -import { updateProfileAction } from "@/app/(app)/onboarding/actions"; +import { updateUserAction } from "@/app/(app)/onboarding/actions"; import { formbricksEnabled, updateResponse } from "@/app/lib/formbricks"; import { cn } from "@formbricks/lib/cn"; import { env } from "@formbricks/lib/env.mjs"; -import { TProfile, TProfileObjective } from "@formbricks/types/profile"; +import { TUser, TUserObjective } from "@formbricks/types/user"; import { Button } from "@formbricks/ui/Button"; import { useEffect, useRef, useState } from "react"; import { toast } from "react-hot-toast"; @@ -14,15 +14,15 @@ type ObjectiveProps = { next: () => void; skip: () => void; formbricksResponseId?: string; - profile: TProfile; + user: TUser; }; type ObjectiveChoice = { label: string; - id: TProfileObjective; + id: TUserObjective; }; -const Objective: React.FC = ({ next, skip, formbricksResponseId, profile }) => { +const Objective: React.FC = ({ next, skip, formbricksResponseId, user }) => { const objectives: Array = [ { label: "Increase conversion", id: "increase_conversion" }, { label: "Improve user retention", id: "improve_user_retention" }, @@ -51,9 +51,9 @@ const Objective: React.FC = ({ next, skip, formbricksResponseId, if (selectedObjective) { try { setIsProfileUpdating(true); - await updateProfileAction({ + await updateUserAction({ objective: selectedObjective.id, - name: profile.name ?? undefined, + name: user.name ?? undefined, }); setIsProfileUpdating(false); } catch (e) { diff --git a/apps/web/app/(app)/onboarding/components/Onboarding.tsx b/apps/web/app/(app)/onboarding/components/Onboarding.tsx index 687ff545d7..2466251c91 100644 --- a/apps/web/app/(app)/onboarding/components/Onboarding.tsx +++ b/apps/web/app/(app)/onboarding/components/Onboarding.tsx @@ -1,8 +1,8 @@ "use client"; -import { updateProfileAction } from "@/app/(app)/onboarding/actions"; +import { updateUserAction } from "@/app/(app)/onboarding/actions"; import { TProduct } from "@formbricks/types/product"; -import { TProfile } from "@formbricks/types/profile"; +import { TUser } from "@formbricks/types/user"; import { Logo } from "@formbricks/ui/Logo"; import { ProgressBar } from "@formbricks/ui/ProgressBar"; import { Session } from "next-auth"; @@ -19,11 +19,11 @@ const MAX_STEPS = 6; interface OnboardingProps { session: Session; environmentId: string; - profile: TProfile; + user: TUser; product: TProduct; } -export default function Onboarding({ session, environmentId, profile, product }: OnboardingProps) { +export default function Onboarding({ session, environmentId, user, product }: OnboardingProps) { const [formbricksResponseId, setFormbricksResponseId] = useState(); const [currentStep, setCurrentStep] = useState(1); const [isLoading, setIsLoading] = useState(false); @@ -53,8 +53,8 @@ export default function Onboarding({ session, environmentId, profile, product }: setIsLoading(true); try { - const updatedProfile = { ...profile, onboardingCompleted: true }; - await updateProfileAction(updatedProfile); + const updatedProfile = { ...user, onboardingCompleted: true }; + await updateUserAction(updatedProfile); if (environmentId) { router.push(`/environments/${environmentId}/surveys`); @@ -85,7 +85,7 @@ export default function Onboarding({ session, environmentId, profile, product }:
{currentStep === 1 && ( - + )} {currentStep === 2 && ( )} {currentStep === 3 && ( - + )} {currentStep === 4 && ( diff --git a/apps/web/app/(app)/onboarding/components/Role.tsx b/apps/web/app/(app)/onboarding/components/Role.tsx index c18f3b77e3..f17ed82003 100644 --- a/apps/web/app/(app)/onboarding/components/Role.tsx +++ b/apps/web/app/(app)/onboarding/components/Role.tsx @@ -1,6 +1,6 @@ "use client"; -import { updateProfileAction } from "@/app/(app)/onboarding/actions"; +import { updateUserAction } from "@/app/(app)/onboarding/actions"; import { createResponse, formbricksEnabled } from "@/app/lib/formbricks"; import { cn } from "@formbricks/lib/cn"; import { env } from "@formbricks/lib/env.mjs"; @@ -49,7 +49,7 @@ const Role: React.FC = ({ next, skip, setFormbricksResponseId, sessio if (selectedRole) { try { setIsUpdating(true); - await updateProfileAction({ role: selectedRole.id }); + await updateUserAction({ role: selectedRole.id }); setIsUpdating(false); } catch (e) { setIsUpdating(false); diff --git a/apps/web/app/(app)/onboarding/page.tsx b/apps/web/app/(app)/onboarding/page.tsx index 11f7fee251..94106f6829 100644 --- a/apps/web/app/(app)/onboarding/page.tsx +++ b/apps/web/app/(app)/onboarding/page.tsx @@ -4,7 +4,7 @@ import { authOptions } from "@formbricks/lib/authOptions"; import { REVALIDATION_INTERVAL } from "@formbricks/lib/constants"; import { getFirstEnvironmentByUserId } from "@formbricks/lib/environment/service"; import { getProductByEnvironmentId } from "@formbricks/lib/product/service"; -import { getProfile } from "@formbricks/lib/profile/service"; +import { getUser } from "@formbricks/lib/user/service"; import { getServerSession } from "next-auth"; import Onboarding from "./components/Onboarding"; import { redirect } from "next/navigation"; @@ -21,12 +21,12 @@ export default async function OnboardingPage() { throw new Error("No environment found for user"); } - const profile = await getProfile(userId); + const user = await getUser(userId); const product = await getProductByEnvironmentId(environment?.id!); - if (!environment || !profile || !product) { - throw new Error("Failed to get environment, profile, or product"); + if (!environment || !user || !product) { + throw new Error("Failed to get environment, user, or product"); } - return ; + return ; } diff --git a/apps/web/app/api/cron/weekly_summary/types.ts b/apps/web/app/api/cron/weekly_summary/types.ts index 9c7de5e928..dbb840e52e 100644 --- a/apps/web/app/api/cron/weekly_summary/types.ts +++ b/apps/web/app/api/cron/weekly_summary/types.ts @@ -1,6 +1,6 @@ import { TResponseData } from "@formbricks/types/responses"; import { TSurveyQuestion, TSurveyStatus } from "@formbricks/types/surveys"; -import { TUserNotificationSettings } from "@formbricks/types/users"; +import { TUserNotificationSettings } from "@formbricks/types/user"; export interface Insights { totalCompletedResponses: number; diff --git a/apps/web/app/api/pipeline/route.ts b/apps/web/app/api/pipeline/route.ts index ac35250e51..d5d4abdd90 100644 --- a/apps/web/app/api/pipeline/route.ts +++ b/apps/web/app/api/pipeline/route.ts @@ -5,7 +5,7 @@ import { prisma } from "@formbricks/database"; import { INTERNAL_SECRET } from "@formbricks/lib/constants"; import { convertDatesInObject } from "@formbricks/lib/time"; import { TSurveyQuestion } from "@formbricks/types/surveys"; -import { TUserNotificationSettings } from "@formbricks/types/users"; +import { TUserNotificationSettings } from "@formbricks/types/user"; import { ZPipelineInput } from "@formbricks/types/pipelines"; import { headers } from "next/headers"; import { NextResponse } from "next/server"; diff --git a/apps/web/app/api/v1/users/route.ts b/apps/web/app/api/v1/users/route.ts index 646ea726a0..b5a200cda3 100644 --- a/apps/web/app/api/v1/users/route.ts +++ b/apps/web/app/api/v1/users/route.ts @@ -6,7 +6,7 @@ import { deleteInvite } from "@formbricks/lib/invite/service"; import { verifyInviteToken } from "@formbricks/lib/jwt"; import { createMembership } from "@formbricks/lib/membership/service"; import { createProduct } from "@formbricks/lib/product/service"; -import { createProfile } from "@formbricks/lib/profile/service"; +import { createUser } from "@formbricks/lib/user/service"; import { createTeam, getTeam } from "@formbricks/lib/team/service"; import { NextResponse } from "next/server"; @@ -23,7 +23,7 @@ export async function POST(request: Request) { let invite; // create the user - user = await createProfile(user); + user = await createUser(user); // User is invited to team if (inviteToken) { diff --git a/apps/web/app/lib/users/users.ts b/apps/web/app/lib/users/users.ts index 06bb8d84e2..fe576735ce 100644 --- a/apps/web/app/lib/users/users.ts +++ b/apps/web/app/lib/users/users.ts @@ -88,7 +88,7 @@ export const resetPassword = async (token: string, password: string): Promise => { +export const deleteUser = async (): Promise => { try { const res = await fetch("/api/v1/users/me/", { method: "DELETE", diff --git a/packages/database/jsonTypes.ts b/packages/database/jsonTypes.ts index 8560d63514..3f9e4996d9 100644 --- a/packages/database/jsonTypes.ts +++ b/packages/database/jsonTypes.ts @@ -13,7 +13,7 @@ import { TSurveyVerifyEmail, } from "@formbricks/types/surveys"; import { TTeamBilling } from "@formbricks/types/teams"; -import { TUserNotificationSettings } from "@formbricks/types/users"; +import { TUserNotificationSettings } from "@formbricks/types/user"; declare global { namespace PrismaJson { diff --git a/packages/database/zod-utils.ts b/packages/database/zod-utils.ts index 65ae087715..2d4c51456d 100644 --- a/packages/database/zod-utils.ts +++ b/packages/database/zod-utils.ts @@ -24,4 +24,4 @@ export { } from "@formbricks/types/surveys"; export { ZTeamBilling } from "@formbricks/types/teams"; -export { ZUserNotificationSettings } from "@formbricks/types/users"; +export { ZUserNotificationSettings } from "@formbricks/types/user"; diff --git a/packages/ee/RoleManagement/lib/actions.ts b/packages/ee/RoleManagement/lib/actions.ts index b10ef99d47..1f95c98a36 100644 --- a/packages/ee/RoleManagement/lib/actions.ts +++ b/packages/ee/RoleManagement/lib/actions.ts @@ -7,16 +7,16 @@ import { updateMembership, } from "@formbricks/lib/membership/service"; import { updateInvite } from "@formbricks/lib/invite/service"; -import { TInviteUpdateInput } from "../../../types/invites"; -import { TMembershipUpdateInput } from "../../../types/memberships"; +import { TInviteUpdateInput } from "@formbricks/types/invites"; +import { TMembershipUpdateInput } from "@formbricks/types/memberships"; import { hasTeamAccess, hasTeamAuthority, isOwner } from "@formbricks/lib/auth"; import { getServerSession } from "next-auth"; -import { AuthenticationError, AuthorizationError, ValidationError } from "../../../types/errors"; -import { TProfile } from "../../../types/profile"; +import { AuthenticationError, AuthorizationError, ValidationError } from "@formbricks/types/errors"; +import { TUser } from "@formbricks/types/user"; export const transferOwnershipAction = async (teamId: string, newOwnerId: string) => { const session = await getServerSession(authOptions); - const user = session?.user as TProfile; + const user = session?.user as TUser; if (!session) { throw new AuthenticationError("Not authenticated"); } @@ -49,7 +49,7 @@ export const transferOwnershipAction = async (teamId: string, newOwnerId: string export const updateInviteAction = async (inviteId: string, teamId: string, data: TInviteUpdateInput) => { const session = await getServerSession(authOptions); - const user = session?.user as TProfile; + const user = session?.user as TUser; if (!user) { throw new AuthenticationError("Not authenticated"); @@ -74,7 +74,7 @@ export const updateMembershipAction = async ( data: TMembershipUpdateInput ) => { const session = await getServerSession(authOptions); - const user = session?.user as TProfile; + const user = session?.user as TUser; if (!user) { throw new AuthenticationError("Not authenticated"); diff --git a/packages/lib/auth/service.ts b/packages/lib/auth/service.ts index 3c8b969e17..9d0892aa51 100644 --- a/packages/lib/auth/service.ts +++ b/packages/lib/auth/service.ts @@ -5,7 +5,7 @@ import { prisma } from "@formbricks/database"; import { symmetricDecrypt, symmetricEncrypt } from "../crypto"; import { verifyPassword } from "../auth"; import { totpAuthenticatorCheck } from "../totp"; -import { profileCache } from "../profile/cache"; +import { userCache } from "../user/cache"; import { ENCRYPTION_KEY } from "../constants"; export const setupTwoFactorAuth = async ( @@ -120,7 +120,7 @@ export const enableTwoFactorAuth = async (id: string, code: string) => { }, }); - profileCache.revalidate({ + userCache.revalidate({ id, }); @@ -221,7 +221,7 @@ export const disableTwoFactorAuth = async (id: string, params: TDisableTwoFactor }, }); - profileCache.revalidate({ + userCache.revalidate({ id, }); diff --git a/packages/lib/authOptions.ts b/packages/lib/authOptions.ts index a74cf97172..0a238b8cdc 100644 --- a/packages/lib/authOptions.ts +++ b/packages/lib/authOptions.ts @@ -12,7 +12,7 @@ import { env } from "./env.mjs"; import { verifyToken } from "./jwt"; import { createMembership } from "./membership/service"; import { createProduct } from "./product/service"; -import { createProfile, getProfileByEmail, updateProfile } from "./profile/service"; +import { createUser, getUserByEmail, updateUser } from "./user/service"; import { createTeam, getTeam } from "./team/service"; export const authOptions: NextAuthOptions = { @@ -110,7 +110,7 @@ export const authOptions: NextAuthOptions = { throw new Error("Email already verified"); } - user = await updateProfile(user.id, { emailVerified: new Date() }); + user = await updateUser(user.id, { emailVerified: new Date() }); return user; }, @@ -132,7 +132,7 @@ export const authOptions: NextAuthOptions = { ], callbacks: { async jwt({ token }) { - const existingUser = await getProfileByEmail(token?.email!); + const existingUser = await getUserByEmail(token?.email!); if (!existingUser) { return token; @@ -191,10 +191,10 @@ export const authOptions: NextAuthOptions = { // check if user with this email already exist // if not found just update user with new email address // if found throw an error (TODO find better solution) - const otherUserWithEmail = await getProfileByEmail(user.email); + const otherUserWithEmail = await getUserByEmail(user.email); if (!otherUserWithEmail) { - await updateProfile(existingUserWithAccount.id, { email: user.email }); + await updateUser(existingUserWithAccount.id, { email: user.email }); return true; } throw new Error( @@ -205,13 +205,13 @@ export const authOptions: NextAuthOptions = { // There is no existing account for this identity provider / account id // check if user account with this email already exists // if user already exists throw error and request password login - const existingUserWithEmail = await getProfileByEmail(user.email); + const existingUserWithEmail = await getUserByEmail(user.email); if (existingUserWithEmail) { throw new Error("A user with this email exists already."); } - const userProfile = await createProfile({ + const userProfile = await createUser({ name: user.name, email: user.email, emailVerified: new Date(Date.now()), diff --git a/packages/lib/membership/hooks/actions.ts b/packages/lib/membership/hooks/actions.ts index 796e62a65c..76d11a115d 100644 --- a/packages/lib/membership/hooks/actions.ts +++ b/packages/lib/membership/hooks/actions.ts @@ -6,12 +6,12 @@ import { authOptions } from "../../authOptions"; import { getTeamByEnvironmentId } from "../../team/service"; import { AuthenticationError } from "@formbricks/types/errors"; import { getMembershipByUserIdTeamId } from "../service"; -import { TProfile } from "@formbricks/types/profile"; +import { TUser } from "@formbricks/types/user"; export const getMembershipByUserIdTeamIdAction = async (environmentId: string) => { const session = await getServerSession(authOptions); const team = await getTeamByEnvironmentId(environmentId); - const user = session?.user as TProfile; + const user = session?.user as TUser; if (!session) { throw new AuthenticationError("Not authenticated"); diff --git a/packages/lib/profile/util.ts b/packages/lib/profile/util.ts deleted file mode 100644 index dd03720642..0000000000 --- a/packages/lib/profile/util.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { TProfile } from "@formbricks/types/profile"; - -export const formatProfileDateFields = (profile: TProfile): TProfile => { - if (typeof profile.createdAt === "string") { - profile.createdAt = new Date(profile.createdAt); - } - if (typeof profile.updatedAt === "string") { - profile.updatedAt = new Date(profile.updatedAt); - } - if (typeof profile.emailVerified === "string") { - profile.emailVerified = new Date(profile.emailVerified); - } - - return profile; -}; diff --git a/packages/lib/profile/cache.ts b/packages/lib/user/cache.ts similarity index 80% rename from packages/lib/profile/cache.ts rename to packages/lib/user/cache.ts index e964eeb591..f7bf2d68a8 100644 --- a/packages/lib/profile/cache.ts +++ b/packages/lib/user/cache.ts @@ -5,13 +5,13 @@ interface RevalidateProps { email?: string; } -export const profileCache = { +export const userCache = { tag: { byId(id: string) { - return `profiles-${id}`; + return `users-${id}`; }, byEmail(email: string) { - return `profiles-${email}`; + return `users-${email}`; }, }, revalidate({ id, email }: RevalidateProps): void { diff --git a/packages/lib/profile/service.ts b/packages/lib/user/service.ts similarity index 64% rename from packages/lib/profile/service.ts rename to packages/lib/user/service.ts index 4631d92177..87fe58de36 100644 --- a/packages/lib/profile/service.ts +++ b/packages/lib/user/service.ts @@ -4,13 +4,7 @@ import { prisma } from "@formbricks/database"; import { ZId } from "@formbricks/types/environment"; import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors"; import { TMembership } from "@formbricks/types/memberships"; -import { - TProfile, - TProfileCreateInput, - TProfileUpdateInput, - ZProfile, - ZProfileUpdateInput, -} from "@formbricks/types/profile"; +import { TUser, TUserCreateInput, TUserUpdateInput, ZUser, ZUserUpdateInput } from "@formbricks/types/user"; import { Prisma } from "@prisma/client"; import { unstable_cache } from "next/cache"; import { z } from "zod"; @@ -19,9 +13,7 @@ import { updateMembership } from "../membership/service"; import { deleteTeam } from "../team/service"; import { formatDateFields } from "../utils/datetime"; import { validateInputs } from "../utils/validate"; -import { profileCache } from "./cache"; -import { formatProfileDateFields } from "./util"; - +import { userCache } from "./cache"; const responseSelection = { id: true, name: true, @@ -36,24 +28,24 @@ const responseSelection = { objective: true, }; -// function to retrive basic information about a user's profile -export const getProfile = async (id: string): Promise => { - const profile = await unstable_cache( +// function to retrive basic information about a user's user +export const getUser = async (id: string): Promise => { + const user = await unstable_cache( async () => { validateInputs([id, ZId]); try { - const profile = await prisma.user.findUnique({ + const user = await prisma.user.findUnique({ where: { id, }, select: responseSelection, }); - if (!profile) { + if (!user) { return null; } - return profile; + return user; } catch (error) { if (error instanceof Prisma.PrismaClientKnownRequestError) { throw new DatabaseError(error.message); @@ -62,35 +54,35 @@ export const getProfile = async (id: string): Promise => { throw error; } }, - [`getProfile-${id}`], + [`getUser-${id}`], { - tags: [profileCache.tag.byId(id)], + tags: [userCache.tag.byId(id)], revalidate: SERVICES_REVALIDATION_INTERVAL, } )(); - return profile + return user ? { - ...profile, - ...formatDateFields(profile, ZProfile), + ...user, + ...formatDateFields(user, ZUser), } : null; }; -export const getProfileByEmail = async (email: string): Promise => { - const profile = await unstable_cache( +export const getUserByEmail = async (email: string): Promise => { + const user = await unstable_cache( async () => { validateInputs([email, z.string().email()]); try { - const profile = await prisma.user.findFirst({ + const user = await prisma.user.findFirst({ where: { email, }, select: responseSelection, }); - return profile; + return user; } catch (error) { if (error instanceof Prisma.PrismaClientKnownRequestError) { throw new DatabaseError(error.message); @@ -99,17 +91,17 @@ export const getProfileByEmail = async (email: string): Promise throw error; } }, - [`getProfileByEmail-${email}`], + [`getUserByEmail-${email}`], { - tags: [profileCache.tag.byEmail(email)], + tags: [userCache.tag.byEmail(email)], revalidate: SERVICES_REVALIDATION_INTERVAL, } )(); - return profile + return user ? { - ...profile, - ...formatProfileDateFields(profile), + ...user, + ...formatDateFields(user, ZUser), } : null; }; @@ -117,12 +109,12 @@ export const getProfileByEmail = async (email: string): Promise const getAdminMemberships = (memberships: TMembership[]): TMembership[] => memberships.filter((membership) => membership.role === "admin"); -// function to update a user's profile -export const updateProfile = async (personId: string, data: TProfileUpdateInput): Promise => { - validateInputs([personId, ZId], [data, ZProfileUpdateInput.partial()]); +// function to update a user's user +export const updateUser = async (personId: string, data: TUserUpdateInput): Promise => { + validateInputs([personId, ZId], [data, ZUserUpdateInput.partial()]); try { - const updatedProfile = await prisma.user.update({ + const updatedUser = await prisma.user.update({ where: { id: personId, }, @@ -130,57 +122,57 @@ export const updateProfile = async (personId: string, data: TProfileUpdateInput) select: responseSelection, }); - profileCache.revalidate({ - email: updatedProfile.email, - id: updatedProfile.id, + userCache.revalidate({ + email: updatedUser.email, + id: updatedUser.id, }); - return updatedProfile; + return updatedUser; } catch (error) { if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2016") { - throw new ResourceNotFoundError("Profile", personId); + throw new ResourceNotFoundError("User", personId); } else { throw error; // Re-throw any other errors } } }; -const deleteUser = async (id: string): Promise => { +const deleteUserById = async (id: string): Promise => { validateInputs([id, ZId]); - const profile = await prisma.user.delete({ + const user = await prisma.user.delete({ where: { id, }, select: responseSelection, }); - profileCache.revalidate({ - email: profile.email, + userCache.revalidate({ + email: user.email, id, }); - return profile; + return user; }; -export const createProfile = async (data: TProfileCreateInput): Promise => { - validateInputs([data, ZProfileUpdateInput]); +export const createUser = async (data: TUserCreateInput): Promise => { + validateInputs([data, ZUserUpdateInput]); - const profile = await prisma.user.create({ + const user = await prisma.user.create({ data: data, select: responseSelection, }); - profileCache.revalidate({ - email: profile.email, - id: profile.id, + userCache.revalidate({ + email: user.email, + id: user.id, }); - return profile; + return user; }; -// function to delete a user's profile including teams -export const deleteProfile = async (id: string): Promise => { +// function to delete a user's user including teams +export const deleteUser = async (id: string): Promise => { validateInputs([id, ZId]); try { @@ -219,9 +211,9 @@ export const deleteProfile = async (id: string): Promise => { } } - const deletedProfile = await deleteUser(id); + const deletedUser = await deleteUserById(id); - return deletedProfile; + return deletedUser; } catch (error) { if (error instanceof Prisma.PrismaClientKnownRequestError) { throw new DatabaseError(error.message); diff --git a/packages/types/auth.ts b/packages/types/auth.ts index b83337838e..1c45a5e77e 100644 --- a/packages/types/auth.ts +++ b/packages/types/auth.ts @@ -1,8 +1,8 @@ import { z } from "zod"; -import { ZProfile } from "./profile"; +import { ZUser } from "./user"; const ZAuthSession = z.object({ - user: ZProfile, + user: ZUser, }); const ZAuthenticationApiKey = z.object({ diff --git a/packages/types/next-auth.d.ts b/packages/types/next-auth.d.ts index 0773fb4968..3584faf2e5 100644 --- a/packages/types/next-auth.d.ts +++ b/packages/types/next-auth.d.ts @@ -1,11 +1,11 @@ import NextAuth from "next-auth"; -import { TProfile } from "./profile"; +import { TUser } from "./user"; declare module "next-auth" { /** * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context */ interface Session { - user: TProfile; + user: TUser; } } diff --git a/packages/types/templates.ts b/packages/types/templates.ts index c2fba539a5..b99ea616e9 100644 --- a/packages/types/templates.ts +++ b/packages/types/templates.ts @@ -1,6 +1,6 @@ import { z } from "zod"; import { ZSurveyWelcomeCard, ZSurveyHiddenFields, ZSurveyQuestions, ZSurveyThankYouCard } from "./surveys"; -import { ZProfileObjective } from "./profile"; +import { ZUserObjective } from "./user"; export const ZTemplate = z.object({ name: z.string(), @@ -9,7 +9,7 @@ export const ZTemplate = z.object({ category: z .enum(["Product Experience", "Exploration", "Growth", "Increase Revenue", "Customer Success"]) .optional(), - objectives: z.array(ZProfileObjective).optional(), + objectives: z.array(ZUserObjective).optional(), preset: z.object({ name: z.string(), welcomeCard: ZSurveyWelcomeCard, diff --git a/packages/types/profile.ts b/packages/types/user.ts similarity index 61% rename from packages/types/profile.ts rename to packages/types/user.ts index e2a3412d19..196f7723b7 100644 --- a/packages/types/profile.ts +++ b/packages/types/user.ts @@ -2,7 +2,7 @@ import z from "zod"; const ZRole = z.enum(["project_manager", "engineer", "founder", "marketing_specialist", "other"]); -export const ZProfileObjective = z.enum([ +export const ZUserObjective = z.enum([ "increase_conversion", "improve_user_retention", "increase_user_adoption", @@ -11,9 +11,9 @@ export const ZProfileObjective = z.enum([ "other", ]); -export type TProfileObjective = z.infer; +export type TUserObjective = z.infer; -export const ZProfile = z.object({ +export const ZUser = z.object({ id: z.string(), name: z.string().nullable(), email: z.string(), @@ -24,32 +24,39 @@ export const ZProfile = z.object({ createdAt: z.date(), updatedAt: z.date(), onboardingCompleted: z.boolean(), - objective: ZProfileObjective.nullable(), + objective: ZUserObjective.nullable(), }); -export type TProfile = z.infer; +export type TUser = z.infer; -export const ZProfileUpdateInput = z.object({ +export const ZUserUpdateInput = z.object({ name: z.string().nullish(), email: z.string().optional(), emailVerified: z.date().nullish(), onboardingCompleted: z.boolean().optional(), role: ZRole.optional(), - objective: ZProfileObjective.nullish(), + objective: ZUserObjective.nullish(), imageUrl: z.string().url().nullish(), }); -export type TProfileUpdateInput = z.infer; +export type TUserUpdateInput = z.infer; -export const ZProfileCreateInput = z.object({ +export const ZUserCreateInput = z.object({ name: z.string().optional(), email: z.string(), emailVerified: z.date().optional(), onboardingCompleted: z.boolean().optional(), role: ZRole.optional(), - objective: ZProfileObjective.nullish(), + objective: ZUserObjective.nullish(), identityProvider: z.enum(["email", "google", "github", "azuread"]).optional(), identityProviderAccountId: z.string().optional(), }); -export type TProfileCreateInput = z.infer; +export type TUserCreateInput = z.infer; + +export const ZUserNotificationSettings = z.object({ + alert: z.record(z.boolean()), + weeklySummary: z.record(z.boolean()), +}); + +export type TUserNotificationSettings = z.infer; diff --git a/packages/types/users.ts b/packages/types/users.ts deleted file mode 100644 index 38c9bc57c7..0000000000 --- a/packages/types/users.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { z } from "zod"; - -export const ZUserNotificationSettings = z.object({ - alert: z.record(z.boolean()), - weeklySummary: z.record(z.boolean()), -}); - -export type TUserNotificationSettings = z.infer; diff --git a/packages/ui/SingleResponseCard/components/ResponseNote.tsx b/packages/ui/SingleResponseCard/components/ResponseNote.tsx index cc7a86580d..3dfa401656 100644 --- a/packages/ui/SingleResponseCard/components/ResponseNote.tsx +++ b/packages/ui/SingleResponseCard/components/ResponseNote.tsx @@ -2,7 +2,7 @@ import { cn } from "@formbricks/lib/cn"; import { timeSince } from "@formbricks/lib/time"; -import { TProfile } from "@formbricks/types/profile"; +import { TUser } from "@formbricks/types/user"; import { TResponseNote } from "@formbricks/types/responses"; import { CheckIcon, PencilIcon, PlusIcon } from "@heroicons/react/24/solid"; import clsx from "clsx"; @@ -15,14 +15,14 @@ import { Button } from "../../Button"; import { resolveResponseNoteAction, updateResponseNoteAction, createResponseNoteAction } from "../actions"; interface ResponseNotesProps { - profile: TProfile; + user: TUser; responseId: string; notes: TResponseNote[]; isOpen: boolean; setIsOpen: (isOpen: boolean) => void; } -export default function ResponseNotes({ profile, responseId, notes, isOpen, setIsOpen }: ResponseNotesProps) { +export default function ResponseNotes({ user, responseId, notes, isOpen, setIsOpen }: ResponseNotesProps) { const router = useRouter(); const [noteText, setNoteText] = useState(""); const [isCreatingNote, setIsCreatingNote] = useState(false); @@ -35,7 +35,7 @@ export default function ResponseNotes({ profile, responseId, notes, isOpen, setI e.preventDefault(); setIsCreatingNote(true); try { - await createResponseNoteAction(responseId, profile.id, noteText); + await createResponseNoteAction(responseId, user.id, noteText); router.refresh(); setIsCreatingNote(false); setNoteText(""); @@ -162,7 +162,7 @@ export default function ResponseNotes({ profile, responseId, notes, isOpen, setI
{note.text} - {profile.id === note.user.id && ( + {user.id === note.user.id && (
{pageType === "response" && (