diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json
index a265afb628..2c096b465e 100644
--- a/apps/web/public/static/locales/en/common.json
+++ b/apps/web/public/static/locales/en/common.json
@@ -1276,6 +1276,7 @@
"personal_cal_url": "My personal {{appName}} URL",
"bio_hint": "A few sentences about yourself. this will appear on your personal url page.",
"user_has_no_bio": "This user has not added a bio yet.",
+ "bio":"Bio",
"delete_account_modal_title": "Delete Account",
"confirm_delete_account_modal": "Are you sure you want to delete your {{appName}} account?",
"delete_my_account": "Delete my account",
diff --git a/packages/features/users/components/UserTable/EditSheet/EditUserForm.tsx b/packages/features/users/components/UserTable/EditSheet/EditUserForm.tsx
index 8e5858241b..3202758a10 100644
--- a/packages/features/users/components/UserTable/EditSheet/EditUserForm.tsx
+++ b/packages/features/users/components/UserTable/EditSheet/EditUserForm.tsx
@@ -1,8 +1,8 @@
import { zodResolver } from "@hookform/resolvers/zod";
import type { Dispatch } from "react";
-import { useForm } from "react-hook-form";
+import { Controller, useForm } from "react-hook-form";
import { z } from "zod";
-import shallow from "zustand/shallow";
+import { shallow } from "zustand/shallow";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc, type RouterOutputs } from "@calcom/trpc/react";
@@ -15,6 +15,7 @@ import {
Label,
showToast,
Avatar,
+ ImageUploader,
} from "@calcom/ui";
import type { Action } from "../UserListTable";
@@ -23,8 +24,9 @@ import { useEditMode } from "./store";
const editSchema = z.object({
name: z.string(),
email: z.string().email(),
+ avatar: z.string(),
bio: z.string(),
- role: z.enum(["ADMIN", "MEMBER"]),
+ role: z.enum(["ADMIN", "MEMBER", "OWNER"]),
timeZone: z.string(),
// schedules: z.array(z.string()),
// teams: z.array(z.string()),
@@ -51,6 +53,7 @@ export function EditForm({
defaultValues: {
name: selectedUser?.name ?? "",
email: selectedUser?.email ?? "",
+ avatar: avatarUrl,
bio: selectedUser?.bio ?? "",
role: selectedUser?.role ?? "",
timeZone: selectedUser?.timeZone ?? "",
@@ -88,12 +91,32 @@ export function EditForm({
role: values.role as "ADMIN" | "MEMBER", // Cast needed as we dont provide an option for owner
name: values.name,
email: values.email,
+ avatar: values.avatar,
bio: values.bio,
timeZone: values.timeZone,
});
}}>
-
-
+
+
(
+
+
+
+ {
+ form.setValue("avatar", newAvatar, { shouldDirty: true });
+ }}
+ imageSrc={value || undefined}
+ />
+
+
+ )}
+ />
{selectedUser?.name ?? "Nameless User"}
diff --git a/packages/features/users/components/UserTable/EditSheet/EditUserSheet.tsx b/packages/features/users/components/UserTable/EditSheet/EditUserSheet.tsx
index c0ab958b19..1c14a0c273 100644
--- a/packages/features/users/components/UserTable/EditSheet/EditUserSheet.tsx
+++ b/packages/features/users/components/UserTable/EditSheet/EditUserSheet.tsx
@@ -97,7 +97,7 @@ export function EditUserSheet({ state, dispatch }: { state: State; dispatch: Dis
) : (
-
+
{username || "No username"}
-
{email}
+
{email}
);
diff --git a/packages/lib/server/queries/organisations/index.ts b/packages/lib/server/queries/organisations/index.ts
index 16d7606718..57127f5d98 100644
--- a/packages/lib/server/queries/organisations/index.ts
+++ b/packages/lib/server/queries/organisations/index.ts
@@ -1,4 +1,5 @@
import prisma from "@calcom/prisma";
+import { MembershipRole } from "@calcom/prisma/enums";
// export type OrganisationWithMembers = Awaited>;
@@ -9,7 +10,7 @@ export async function isOrganisationAdmin(userId: number, orgId: number) {
where: {
userId,
teamId: orgId,
- OR: [{ role: "ADMIN" }, { role: "OWNER" }],
+ OR: [{ role: MembershipRole.ADMIN }, { role: MembershipRole.OWNER }],
},
})) || false
);
@@ -19,7 +20,7 @@ export async function isOrganisationOwner(userId: number, orgId: number) {
where: {
userId,
teamId: orgId,
- role: "OWNER",
+ role: MembershipRole.OWNER,
},
}));
}
diff --git a/packages/trpc/server/routers/viewer/organizations/updateUser.handler.ts b/packages/trpc/server/routers/viewer/organizations/updateUser.handler.ts
index 115324f1b5..35f17b9435 100644
--- a/packages/trpc/server/routers/viewer/organizations/updateUser.handler.ts
+++ b/packages/trpc/server/routers/viewer/organizations/updateUser.handler.ts
@@ -1,5 +1,7 @@
-import { isOrganisationAdmin } from "@calcom/lib/server/queries/organisations";
+import { isOrganisationAdmin, isOrganisationOwner } from "@calcom/lib/server/queries/organisations";
+import { resizeBase64Image } from "@calcom/lib/server/resizeBase64Image";
import { prisma } from "@calcom/prisma";
+import { MembershipRole } from "@calcom/prisma/enums";
import type { TrpcSessionUser } from "@calcom/trpc/server/trpc";
import { TRPCError } from "@trpc/server";
@@ -21,6 +23,11 @@ export const updateUserHandler = async ({ ctx, input }: UpdateUserOptions) => {
if (!(await isOrganisationAdmin(userId, organizationId))) throw new TRPCError({ code: "UNAUTHORIZED" });
+ // only OWNER can update the role to OWNER
+ if (input.role === MembershipRole.OWNER && !(await isOrganisationOwner(userId, organizationId))) {
+ throw new TRPCError({ code: "UNAUTHORIZED" });
+ }
+
// Is requested user a member of the organization?
const requestedMember = await prisma.membership.findFirst({
where: {
@@ -33,6 +40,11 @@ export const updateUserHandler = async ({ ctx, input }: UpdateUserOptions) => {
if (!requestedMember)
throw new TRPCError({ code: "UNAUTHORIZED", message: "User does not belong to your organization" });
+ let avatar = input.avatar;
+ if (input.avatar) {
+ avatar = await resizeBase64Image(input.avatar);
+ }
+
// Update user
await prisma.$transaction([
prisma.user.update({
@@ -44,6 +56,7 @@ export const updateUserHandler = async ({ ctx, input }: UpdateUserOptions) => {
email: input.email,
name: input.name,
timeZone: input.timeZone,
+ avatar,
},
}),
prisma.membership.update({
diff --git a/packages/trpc/server/routers/viewer/organizations/updateUser.schema.ts b/packages/trpc/server/routers/viewer/organizations/updateUser.schema.ts
index 3f7fcde07b..e12658864e 100644
--- a/packages/trpc/server/routers/viewer/organizations/updateUser.schema.ts
+++ b/packages/trpc/server/routers/viewer/organizations/updateUser.schema.ts
@@ -5,7 +5,8 @@ export const ZUpdateUserInputSchema = z.object({
bio: z.string().optional(),
name: z.string().optional(),
email: z.string().optional(),
- role: z.enum(["ADMIN", "MEMBER"]),
+ avatar: z.string().optional(),
+ role: z.enum(["ADMIN", "MEMBER", "OWNER"]),
timeZone: z.string(),
});
diff --git a/packages/ui/components/sheet/sheet.tsx b/packages/ui/components/sheet/sheet.tsx
index e0e6827780..4e51112030 100644
--- a/packages/ui/components/sheet/sheet.tsx
+++ b/packages/ui/components/sheet/sheet.tsx
@@ -150,7 +150,7 @@ const SheetContent = React.forwardRef
- {children}
+ {children}
{bottomActions && (