From aabfd3b3f6764d41e5eb98a27455ebfeb2f41b90 Mon Sep 17 00:00:00 2001 From: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Date: Tue, 16 Jan 2024 23:18:17 +1300 Subject: [PATCH] chore: Chore team metadata table + isOrganization migration from metadata (#12828) * Migration+schema changes * Remove org utils from schema * Users fixture * Auth signup handlers * checkIsOrg fn * use team.isOrganization > metadata * Add isOrganization to getTeamWithMembers query * list handler + optimise queries * Update trpc handlers to use team.isOrganization && orgSettings table * Update fixture * Fix typo in prisma.schema * next auth options use organizationSettings instread of tema metadata * Admin tooling * Updating components to use orgsettings over metadata * Add unique index + fix org lib files * validateUsername * Add a seperate unique constraint between isOrg and slug * Invite Member Logic * fix resend file * force isOrg to exist * Team settings * update createOrUpdateMemberships * fix: admin get handler * fix: team list handler - improve select * fix: update admin update handler * fix: settings layout - team get select * organziationSettings Schema + adminupdate * organziationSettings Schema + adminupdate * typefix onSubtmi * Fix:team repo throwing error if no metadata is found * fix: add onDelete to be cascade instead of restri * fix: org - team invite e2e * fix: team invites not working in subteam context * rename to organizationID * fix user fixture team names * Fix the organization created in tests * fix: update organization metadata * variable rename * Reuse isOrganization * variable rename * Remove unnecessary optional chaining * Update packages/trpc/server/routers/viewer/organizations/adminUpdate.handler.ts Co-authored-by: Hariom Balhara * Update packages/trpc/server/routers/viewer/organizations/adminUpdate.handler.ts Co-authored-by: Hariom Balhara * Update packages/trpc/server/routers/viewer/organizations/create.handler.ts --------- Co-authored-by: Hariom Co-authored-by: Keith Williams --- apps/web/lib/orgMigration.test.ts | 26 ++++++----- apps/web/lib/orgMigration.ts | 23 ++++++---- apps/web/pages/signup.tsx | 16 +++---- apps/web/pages/team/[slug].tsx | 10 ++--- apps/web/playwright/fixtures/orgs.ts | 2 +- apps/web/playwright/fixtures/users.ts | 26 +++++------ .../features/auth/lib/next-auth-options.ts | 18 ++------ .../auth/signup/handlers/calcomHandler.ts | 5 +-- .../auth/signup/handlers/selfHostedHandler.ts | 4 -- .../signup/utils/createOrUpdateMemberships.ts | 9 +--- .../features/ee/organizations/api/subteams.ts | 10 +++-- .../ee/organizations/lib/orgDomains.ts | 5 +-- .../pages/settings/admin/AdminOrgEditPage.tsx | 10 +++-- .../pages/settings/admin/AdminOrgPage.tsx | 10 ++--- packages/features/ee/teams/api/upgrade.ts | 2 +- .../ee/teams/components/MemberListItem.tsx | 5 +-- .../filters/components/TeamsFilter.tsx | 3 +- packages/lib/entityPermissionUtils.ts | 5 +-- packages/lib/server/queries/teams/index.ts | 1 + packages/lib/server/repository/team.test.ts | 36 ++++++---------- packages/lib/server/repository/team.ts | 34 +++++++++------ packages/lib/validateUsername.ts | 13 +++--- .../migration.sql | 43 +++++++++++++++++++ packages/prisma/schema.prisma | 12 ++++++ packages/prisma/zod-utils.ts | 11 +++-- .../teamsAndUserProfilesQuery.handler.ts | 5 ++- .../viewer/eventTypes/getByViewer.handler.ts | 7 ++- .../viewer/organizations/adminGet.handler.ts | 10 ++++- .../organizations/adminGetAll.handler.ts | 10 +---- .../organizations/adminUpdate.handler.ts | 39 +++++++++++++---- .../organizations/adminUpdate.schema.ts | 4 +- .../organizations/adminVerify.handler.ts | 17 ++------ .../checkIfOrgNeedsUpgrade.handler.ts | 2 +- .../viewer/organizations/create.handler.ts | 12 ++++-- .../viewer/organizations/update.schema.ts | 2 +- .../viewer/teams/createInvite.handler.ts | 4 +- .../routers/viewer/teams/delete.handler.ts | 5 +-- .../viewer/teams/getUpgradeable.handler.ts | 2 +- .../inviteMember/inviteMember.handler.ts | 24 +++++------ .../inviteMember/inviteMemberUtils.test.ts | 12 +++++- .../viewer/teams/inviteMember/utils.ts | 41 +++++++++++------- .../routers/viewer/teams/list.handler.ts | 12 ++++-- .../viewer/teams/listOwnedTeams.handler.ts | 4 +- .../viewer/teams/removeMember.handler.ts | 14 +++--- .../viewer/teams/resendInvitation.handler.ts | 2 +- .../viewer/webhook/getByViewer.handler.ts | 5 +-- 46 files changed, 323 insertions(+), 249 deletions(-) create mode 100644 packages/prisma/migrations/20231215054122_organization_metadata/migration.sql diff --git a/apps/web/lib/orgMigration.test.ts b/apps/web/lib/orgMigration.test.ts index 080ac4131d..a107a8012a 100644 --- a/apps/web/lib/orgMigration.test.ts +++ b/apps/web/lib/orgMigration.test.ts @@ -696,7 +696,7 @@ describe("orgMigration", () => { }); describe("when user email matches orgAutoAcceptEmail", () => { - const orgMetadata = { + const orgSettings = { orgAutoAcceptEmail: "org1.com", } as const; @@ -725,8 +725,10 @@ describe("orgMigration", () => { const dbOrg = await createOrg({ slug: data.targetOrg.slug, name: data.targetOrg.name, - metadata: { - ...orgMetadata, + organizationSettings: { + create: { + ...orgSettings, + }, }, }); @@ -784,8 +786,10 @@ describe("orgMigration", () => { const dbOrg = await createOrg({ slug: data.targetOrg.slug, name: data.targetOrg.name, - metadata: { - ...orgMetadata, + organizationSettings: { + create: { + ...orgSettings, + }, }, }); @@ -837,7 +841,11 @@ describe("orgMigration", () => { name: data.targetOrg.name, metadata: { requestedSlug: data.targetOrg.requestedSlug, - ...orgMetadata, + }, + organizationSettings: { + create: { + ...orgSettings, + }, }, }); @@ -901,9 +909,7 @@ describe("orgMigration", () => { id: data.targetOrg.id, slug: data.targetOrg.slug, name: data.targetOrg.name, - metadata: { - isOrganization: true, - }, + isOrganization: true, }, }); @@ -1564,9 +1570,9 @@ async function createOrg( return await prismock.team.create({ data: { ...data, + isOrganization: true, metadata: { ...(data.metadata || {}), - isOrganization: true, }, }, }); diff --git a/apps/web/lib/orgMigration.ts b/apps/web/lib/orgMigration.ts index c09304c315..ef1921d840 100644 --- a/apps/web/lib/orgMigration.ts +++ b/apps/web/lib/orgMigration.ts @@ -1,5 +1,6 @@ import { getOrgUsernameFromEmail } from "@calcom/features/auth/signup/utils/getOrgUsernameFromEmail"; import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains"; +import { isOrganization } from "@calcom/lib/entityPermissionUtils"; import { HttpError } from "@calcom/lib/http-error"; import logger from "@calcom/lib/logger"; import { safeStringify } from "@calcom/lib/safeStringify"; @@ -46,7 +47,7 @@ export async function moveUserToOrg({ const teamMetadata = teamMetadataSchema.parse(team?.metadata); - if (!teamMetadata?.isOrganization) { + if (!isOrganization({ team })) { throw new Error(`Team with ID:${targetOrgId} is not an Org`); } @@ -60,7 +61,7 @@ export async function moveUserToOrg({ if (!targetOrgUsername) { targetOrgUsername = getOrgUsernameFromEmail( userToMoveToOrg.email, - targetOrganization.metadata.orgAutoAcceptEmail || "" + team.organizationSettings?.orgAutoAcceptEmail || "" ); } @@ -195,7 +196,7 @@ export async function moveTeamToOrg({ const teamMetadata = teamMetadataSchema.parse(possibleOrg?.metadata); - if (!teamMetadata?.isOrganization) { + if (!isOrganization({ team: possibleOrg })) { throw new Error(`${targetOrg.id} is not an Org`); } @@ -204,7 +205,7 @@ export async function moveTeamToOrg({ await addTeamRedirect({ oldTeamSlug, teamSlug: updatedTeam.slug, - orgSlug: targetOrganization.slug || orgMetadata.requestedSlug || null, + orgSlug: targetOrganization.slug || orgMetadata?.requestedSlug || null, }); await setOrgSlugIfNotSet({ slug: targetOrganization.slug }, orgMetadata, targetOrg.id); if (moveMembers) { @@ -328,13 +329,14 @@ async function setOrgSlugIfNotSet( }, orgMetadata: { requestedSlug?: string | undefined; - }, + } | null, targetOrgId: number ) { if (targetOrganization.slug) { return; } - if (!orgMetadata.requestedSlug) { + + if (!orgMetadata?.requestedSlug) { throw new HttpError({ statusCode: 400, message: `Org with id: ${targetOrgId} doesn't have a slug. Tried using requestedSlug but that's also not present. So, all migration done but failed to set the Organization slug. Please set it manually`, @@ -371,6 +373,9 @@ async function getTeamOrThrowError(targetOrgId: number) { where: { id: targetOrgId, }, + include: { + organizationSettings: true, + }, }); if (!team) { @@ -620,6 +625,7 @@ async function moveTeamsWithoutMembersToOrg({ id: true, slug: true, metadata: true, + isOrganization: true, }, }); @@ -631,7 +637,7 @@ async function moveTeamsWithoutMembersToOrg({ }; }) // Remove Orgs from the list - .filter((team) => !team.metadata?.isOrganization); + .filter((team) => !isOrganization({ team })); const teamIdsToBeMovedToOrg = teamsToBeMovedToOrg.map((t) => t.id); @@ -784,6 +790,7 @@ async function removeTeamsWithoutItsMemberFromOrg({ userToRemoveFromOrg }: { use id: true, slug: true, metadata: true, + isOrganization: true, }, }); @@ -795,7 +802,7 @@ async function removeTeamsWithoutItsMemberFromOrg({ userToRemoveFromOrg }: { use }; }) // Remove Orgs from the list - .filter((team) => !team.metadata?.isOrganization); + .filter((team) => !isOrganization({ team })); const teamIdsToBeRemovedFromOrg = teamsToBeRemovedFromOrg.map((t) => t.id); diff --git a/apps/web/pages/signup.tsx b/apps/web/pages/signup.tsx index 1230b038ac..1c671b6bab 100644 --- a/apps/web/pages/signup.tsx +++ b/apps/web/pages/signup.tsx @@ -609,14 +609,16 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { team: { select: { metadata: true, + isOrganization: true, parentId: true, parent: { select: { slug: true, - metadata: true, + organizationSettings: true, }, }, slug: true, + organizationSettings: true, }, }, }, @@ -665,8 +667,8 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { }; const isATeamInOrganization = tokenTeam?.parentId !== null; - const isOrganization = tokenTeam.metadata?.isOrganization; // Detect if the team is an org by either the metadata flag or if it has a parent team + const isOrganization = tokenTeam.isOrganization; const isOrganizationOrATeamInOrganization = isOrganization || isATeamInOrganization; // If we are dealing with an org, the slug may come from the team itself or its parent const orgSlug = isOrganizationOrATeamInOrganization @@ -683,9 +685,7 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { const isValidEmail = checkValidEmail(verificationToken.identifier); const isOrgInviteByLink = isOrganizationOrATeamInOrganization && !isValidEmail; - const parentMetaDataForSubteam = tokenTeam?.parent?.metadata - ? teamMetadataSchema.parse(tokenTeam.parent.metadata) - : null; + const parentOrgSettings = tokenTeam?.parent?.organizationSettings ?? null; return { props: { @@ -698,15 +698,15 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { ? getOrgUsernameFromEmail( verificationToken.identifier, (isOrganization - ? tokenTeam.metadata?.orgAutoAcceptEmail - : parentMetaDataForSubteam?.orgAutoAcceptEmail) || "" + ? tokenTeam.organizationSettings?.orgAutoAcceptEmail + : parentOrgSettings?.orgAutoAcceptEmail) || "" ) : slugify(username), } : null, orgSlug, orgAutoAcceptEmail: isOrgInviteByLink - ? tokenTeam?.metadata?.orgAutoAcceptEmail ?? parentMetaDataForSubteam?.orgAutoAcceptEmail ?? null + ? tokenTeam?.organizationSettings?.orgAutoAcceptEmail ?? parentOrgSettings?.orgAutoAcceptEmail ?? null : null, }, }; diff --git a/apps/web/pages/team/[slug].tsx b/apps/web/pages/team/[slug].tsx index 6b80836974..830acfb6ef 100644 --- a/apps/web/pages/team/[slug].tsx +++ b/apps/web/pages/team/[slug].tsx @@ -74,7 +74,7 @@ function TeamPage({ return (
@@ -178,7 +178,7 @@ function TeamPage({ title: markdownStrippedBio, profile: { name: `${team.name}`, - image: `${WEBAPP_URL}/${team.metadata?.isOrganization ? "org" : "team"}/${team.slug}/avatar.png`, + image: `${WEBAPP_URL}/${team.isOrganization ? "org" : "team"}/${team.slug}/avatar.png`, }, }} /> @@ -190,7 +190,7 @@ function TeamPage({ imageSrc={ isValidOrgDomain ? `/org/${currentOrgDomain}/avatar.png` - : `${WEBAPP_URL}/${team.metadata?.isOrganization ? "org" : "team"}/${team.slug}/avatar.png` + : `${WEBAPP_URL}/${team.isOrganization ? "org" : "team"}/${team.slug}/avatar.png` } size="lg" /> @@ -208,7 +208,7 @@ function TeamPage({ )} - {metadata?.isOrganization ? ( + {team?.isOrganization ? ( ) : ( <> @@ -311,7 +311,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => // Taking care of sub-teams and orgs if ( (!isValidOrgDomain && team?.parent) || - (!isValidOrgDomain && !!metadata?.isOrganization) || + (!isValidOrgDomain && !!team?.isOrganization) || !isOrganizationFeatureEnabled ) { return { notFound: true } as const; diff --git a/apps/web/playwright/fixtures/orgs.ts b/apps/web/playwright/fixtures/orgs.ts index 265fcd73a1..ea67350304 100644 --- a/apps/web/playwright/fixtures/orgs.ts +++ b/apps/web/playwright/fixtures/orgs.ts @@ -43,8 +43,8 @@ async function createOrgInDb({ data: { name: name, slug: slug, + isOrganization: true, metadata: { - isOrganization: true, ...(requestedSlug ? { requestedSlug, diff --git a/apps/web/playwright/fixtures/users.ts b/apps/web/playwright/fixtures/users.ts index 4d6788330b..cc7473c725 100644 --- a/apps/web/playwright/fixtures/users.ts +++ b/apps/web/playwright/fixtures/users.ts @@ -104,18 +104,21 @@ const createTeamAndAddUser = async ( const slug = `${isOrg ? "org" : "team"}-${workerInfo.workerIndex}-${Date.now()}`; const data: PrismaType.TeamCreateInput = { name: `user-id-${user.id}'s ${isOrg ? "Org" : "Team"}`, + isOrganization: isOrg, }; data.metadata = { ...(isUnpublished ? { requestedSlug: slug } : {}), - ...(isOrg - ? { - isOrganization: true, - isOrganizationVerified: !!isOrgVerified, - orgAutoAcceptEmail: user.email.split("@")[1], - isOrganizationConfigured: false, - } - : {}), }; + if (isOrg) { + data.organizationSettings = { + create: { + isOrganizationVerified: !!isOrgVerified, + orgAutoAcceptEmail: user.email.split("@")[1], + isOrganizationConfigured: false, + }, + }; + } + data.slug = !isUnpublished ? slug : undefined; if (isOrg && hasSubteam) { const team = await createTeamAndAddUser({ user }, workerInfo); @@ -501,7 +504,7 @@ const createUserFixture = (user: UserWithIncludes, page: Page) => { }, }; }) - .find((membership) => !membership.team?.metadata?.isOrganization); + .find((membership) => !membership.team.isOrganization); if (!membership) { throw new Error("No team found for user"); } @@ -512,10 +515,7 @@ const createUserFixture = (user: UserWithIncludes, page: Page) => { where: { userId: user.id, team: { - metadata: { - path: ["isOrganization"], - equals: true, - }, + isOrganization: true, }, }, include: { team: { include: { children: true } } }, diff --git a/packages/features/auth/lib/next-auth-options.ts b/packages/features/auth/lib/next-auth-options.ts index 0cf2530174..113bb510b8 100644 --- a/packages/features/auth/lib/next-auth-options.ts +++ b/packages/features/auth/lib/next-auth-options.ts @@ -61,20 +61,10 @@ const checkIfUserShouldBelongToOrg = async (idP: IdentityProvider, email: string if (!ORGANIZATIONS_AUTOLINK || idP !== "GOOGLE") return { orgUsername, orgId: undefined }; const existingOrg = await prisma.team.findFirst({ where: { - AND: [ - { - metadata: { - path: ["isOrganizationVerified"], - equals: true, - }, - }, - { - metadata: { - path: ["orgAutoAcceptEmail"], - equals: apexDomain, - }, - }, - ], + organizationSettings: { + isOrganizationVerified: true, + orgAutoAcceptEmail: apexDomain, + }, }, select: { id: true, diff --git a/packages/features/auth/signup/handlers/calcomHandler.ts b/packages/features/auth/signup/handlers/calcomHandler.ts index 47e86e779a..d95f1d5352 100644 --- a/packages/features/auth/signup/handlers/calcomHandler.ts +++ b/packages/features/auth/signup/handlers/calcomHandler.ts @@ -14,7 +14,7 @@ import { closeComUpsertTeamUser } from "@calcom/lib/sync/SyncServiceManager"; import { validateAndGetCorrectedUsernameAndEmail } from "@calcom/lib/validateUsername"; import { prisma } from "@calcom/prisma"; import { IdentityProvider } from "@calcom/prisma/enums"; -import { signupSchema, teamMetadataSchema } from "@calcom/prisma/zod-utils"; +import { signupSchema } from "@calcom/prisma/zod-utils"; import { joinAnyChildTeamOnOrgInvite } from "../utils/organization"; import { @@ -129,8 +129,6 @@ async function handler(req: RequestWithUsernameStatus, res: NextApiResponse) { }, }); if (team) { - const teamMetadata = teamMetadataSchema.parse(team?.metadata); - const user = await prisma.user.upsert({ where: { email }, update: { @@ -149,7 +147,6 @@ async function handler(req: RequestWithUsernameStatus, res: NextApiResponse) { // Wrapping in a transaction as if one fails we want to rollback the whole thing to preventa any data inconsistencies const { membership } = await createOrUpdateMemberships({ - teamMetadata, user, team, }); diff --git a/packages/features/auth/signup/handlers/selfHostedHandler.ts b/packages/features/auth/signup/handlers/selfHostedHandler.ts index 60e1111432..3ccb2fc498 100644 --- a/packages/features/auth/signup/handlers/selfHostedHandler.ts +++ b/packages/features/auth/signup/handlers/selfHostedHandler.ts @@ -12,7 +12,6 @@ import { validateAndGetCorrectedUsernameAndEmail } from "@calcom/lib/validateUse import prisma from "@calcom/prisma"; import { IdentityProvider } from "@calcom/prisma/enums"; import { signupSchema } from "@calcom/prisma/zod-utils"; -import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; import { joinAnyChildTeamOnOrgInvite } from "../utils/organization"; import { @@ -69,8 +68,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) }, }); if (team) { - const teamMetadata = teamMetadataSchema.parse(team?.metadata); - const user = await prisma.user.upsert({ where: { email: userEmail }, update: { @@ -88,7 +85,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) }); const { membership } = await createOrUpdateMemberships({ - teamMetadata, user, team, }); diff --git a/packages/features/auth/signup/utils/createOrUpdateMemberships.ts b/packages/features/auth/signup/utils/createOrUpdateMemberships.ts index 593fb50ebd..2380da13a6 100644 --- a/packages/features/auth/signup/utils/createOrUpdateMemberships.ts +++ b/packages/features/auth/signup/utils/createOrUpdateMemberships.ts @@ -1,21 +1,16 @@ -import type z from "zod"; - import { prisma } from "@calcom/prisma"; import type { Team, User } from "@calcom/prisma/client"; import { MembershipRole } from "@calcom/prisma/enums"; -import type { teamMetadataSchema } from "@calcom/prisma/zod-utils"; export const createOrUpdateMemberships = async ({ - teamMetadata, user, team, }: { user: Pick; - team: Pick; - teamMetadata: z.infer; + team: Pick; }) => { return await prisma.$transaction(async (tx) => { - if (teamMetadata?.isOrganization) { + if (team.isOrganization) { await tx.user.update({ where: { id: user.id, diff --git a/packages/features/ee/organizations/api/subteams.ts b/packages/features/ee/organizations/api/subteams.ts index 352b922bb4..ad935527c2 100644 --- a/packages/features/ee/organizations/api/subteams.ts +++ b/packages/features/ee/organizations/api/subteams.ts @@ -4,7 +4,6 @@ import z from "zod"; import { HttpError } from "@calcom/lib/http-error"; import { defaultHandler, defaultResponder } from "@calcom/lib/server"; import prisma from "@calcom/prisma"; -import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; const querySchema = z.object({ org: z.string({ required_error: "org slug is required" }), @@ -20,13 +19,16 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { } = parsedQuery; if (!slug) return res.status(400).json({ message: "Org is needed" }); - const org = await prisma.team.findFirst({ where: { slug }, select: { children: true, metadata: true } }); + const org = await prisma.team.findFirst({ + where: { slug }, + select: { children: true, isOrganization: true }, + }); if (!org) return res.status(400).json({ message: "Org doesn't exist" }); - const metadata = teamMetadataSchema.parse(org?.metadata); + const isOrganization = org.isOrganization; - if (!metadata?.isOrganization) return res.status(400).json({ message: "Team is not an org" }); + if (!isOrganization) return res.status(400).json({ message: "Team is not an org" }); return res.status(200).json({ slugs: org.children.map((ch) => ch.slug) }); } diff --git a/packages/features/ee/organizations/lib/orgDomains.ts b/packages/features/ee/organizations/lib/orgDomains.ts index ebb0c9d73e..6a2cbc1050 100644 --- a/packages/features/ee/organizations/lib/orgDomains.ts +++ b/packages/features/ee/organizations/lib/orgDomains.ts @@ -139,10 +139,7 @@ export function whereClauseForOrgWithSlugOrRequestedSlug(slug: string) { }, }, ], - metadata: { - path: ["isOrganization"], - equals: true, - }, + isOrganization: true, } satisfies Prisma.TeamWhereInput; } diff --git a/packages/features/ee/organizations/pages/settings/admin/AdminOrgEditPage.tsx b/packages/features/ee/organizations/pages/settings/admin/AdminOrgEditPage.tsx index 620210633e..4dd43bb817 100644 --- a/packages/features/ee/organizations/pages/settings/admin/AdminOrgEditPage.tsx +++ b/packages/features/ee/organizations/pages/settings/admin/AdminOrgEditPage.tsx @@ -6,7 +6,7 @@ import { z } from "zod"; import NoSSR from "@calcom/core/components/NoSSR"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useParamsWithFallback } from "@calcom/lib/hooks/useParamsWithFallback"; -import type { teamMetadataSchema } from "@calcom/prisma/zod-utils"; +import type { orgSettingsSchema } from "@calcom/prisma/zod-utils"; import { trpc } from "@calcom/trpc/react"; import { Button, Form, Meta, TextField, showToast } from "@calcom/ui"; @@ -43,7 +43,7 @@ const OrgEditView = ({ orgId }: { orgId: number }) => { type FormValues = { name: Team["name"]; slug: Team["slug"]; - metadata: z.infer; + organizationSettings: z.infer; }; const OrgForm = ({ @@ -81,6 +81,10 @@ const OrgForm = ({ mutation.mutate({ id: org.id, ...values, + organizationSettings: { + ...org.organizationSettings, + orgAutoAcceptEmail: values.organizationSettings?.orgAutoAcceptEmail, + }, }); }; @@ -96,7 +100,7 @@ const OrgForm = ({ label="Domain for which invitations are auto-accepted" placeholder="abc.com" required - {...form.register("metadata.orgAutoAcceptEmail")} + {...form.register("organizationSettings.orgAutoAcceptEmail")} />