diff --git a/apps/web/playwright/settings/upload-avatar.e2e.ts b/apps/web/playwright/settings/upload-avatar.e2e.ts index adcf31df501032..5211e9b50dc449 100644 --- a/apps/web/playwright/settings/upload-avatar.e2e.ts +++ b/apps/web/playwright/settings/upload-avatar.e2e.ts @@ -155,7 +155,7 @@ test.describe("Organization Logo", async () => { await page.getByTestId("upload-avatar").click(); - await page.getByText("Update").click(); + await page.getByTestId("update-org-profile-button").click(); await page.waitForSelector("text=Your organization updated successfully"); const response = await prisma.avatar.findUniqueOrThrow({ diff --git a/packages/features/ee/organizations/pages/settings/appearance.tsx b/packages/features/ee/organizations/pages/settings/appearance.tsx index ec1d536e934095..93ec42807d265f 100644 --- a/packages/features/ee/organizations/pages/settings/appearance.tsx +++ b/packages/features/ee/organizations/pages/settings/appearance.tsx @@ -5,20 +5,17 @@ import { useRouter } from "next/navigation"; import { useState, useEffect } from "react"; import { useForm } from "react-hook-form"; -import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired"; import BrandColorsForm from "@calcom/features/ee/components/BrandColorsForm"; import { AppearanceSkeletonLoader } from "@calcom/features/ee/components/CommonSkeletonLoaders"; import SectionBottomActions from "@calcom/features/settings/SectionBottomActions"; import ThemeLabel from "@calcom/features/settings/ThemeLabel"; -import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout"; import { DEFAULT_LIGHT_BRAND_COLOR, DEFAULT_DARK_BRAND_COLOR } from "@calcom/lib/constants"; import { APP_NAME } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { MembershipRole } from "@calcom/prisma/enums"; import { trpc } from "@calcom/trpc/react"; import type { RouterOutputs } from "@calcom/trpc/react"; -import { Button, Form, Meta, showToast, SettingsToggle, Avatar, ImageUploader } from "@calcom/ui"; -import { Icon } from "@calcom/ui"; +import { Button, Form, showToast, SettingsToggle } from "@calcom/ui"; type BrandColorsFormValues = { brandColor: string; @@ -27,7 +24,6 @@ type BrandColorsFormValues = { const OrgAppearanceView = ({ currentOrg, - isAdminOrOwner, }: { currentOrg: RouterOutputs["viewer"]["organizations"]["listCurrent"]; isAdminOrOwner: boolean; @@ -79,134 +75,79 @@ const OrgAppearanceView = ({ }; return ( - - - {isAdminOrOwner ? ( -
-
-
- } - size="lg" - /> -
-
- { - mutation.mutate({ - calVideoLogo: newLogo, - }); - }} - disabled={mutation.isPending} - imageSrc={currentOrg?.calVideoLogo ?? undefined} - uploadInstruction={t("cal_video_logo_upload_instruction")} - triggerButtonColor={currentOrg?.calVideoLogo ? "secondary" : "primary"} - /> - {currentOrg?.calVideoLogo && ( - - )} -
-
-
+
+
{ + mutation.mutate({ + theme: value.theme ?? null, + }); + }}> +
+
+

{t("theme")}

+

{t("theme_applies_note")}

- { - mutation.mutate({ - theme: value.theme ?? null, - }); - }}> -
-
-

{t("theme")}

-

{t("theme_applies_note")}

-
-
-
- - - -
- - - - - -
{ - onBrandColorsFormSubmit(values); - }}> - - - - { - setHideBrandingValue(checked); - mutation.mutate({ hideBranding: checked }); - }} - switchContainerClassName="mt-6" - />
- ) : ( -
- {t("only_owner_change")} +
+ + +
- )} - + + + + + +
{ + onBrandColorsFormSubmit(values); + }}> + + + + { + setHideBrandingValue(checked); + mutation.mutate({ hideBranding: checked }); + }} + switchContainerClassName="mt-6" + /> +
); }; @@ -237,6 +178,4 @@ const OrgAppearanceViewWrapper = () => { return ; }; -OrgAppearanceViewWrapper.getLayout = getLayout; - export default OrgAppearanceViewWrapper; diff --git a/packages/features/ee/organizations/pages/settings/profile.tsx b/packages/features/ee/organizations/pages/settings/profile.tsx index 1a24b25b231227..fd35ca5cefae15 100644 --- a/packages/features/ee/organizations/pages/settings/profile.tsx +++ b/packages/features/ee/organizations/pages/settings/profile.tsx @@ -9,6 +9,7 @@ import { z } from "zod"; import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired"; import { subdomainSuffix } from "@calcom/features/ee/organizations/lib/orgDomains"; +import OrgAppearanceViewWrapper from "@calcom/features/ee/organizations/pages/settings/appearance"; import SectionBottomActions from "@calcom/features/settings/SectionBottomActions"; import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage"; import { useLocale } from "@calcom/lib/hooks/useLocale"; @@ -16,6 +17,7 @@ import { md } from "@calcom/lib/markdownIt"; import turndown from "@calcom/lib/turndownService"; import { MembershipRole } from "@calcom/prisma/enums"; import { trpc } from "@calcom/trpc/react"; +import { Icon } from "@calcom/ui"; import { Avatar, BannerUploader, @@ -41,6 +43,7 @@ const orgProfileFormSchema = z.object({ name: z.string(), logoUrl: z.string().nullable(), banner: z.string().nullable(), + calVideoLogo: z.string().nullable(), bio: z.string(), }); @@ -50,6 +53,7 @@ type FormValues = { banner: string | null; bio: string; slug: string; + calVideoLogo: string | null; }; const SkeletonLoader = ({ title, description }: { title: string; description: string }) => { @@ -114,6 +118,7 @@ const OrgProfileView = () => { logoUrl: currentOrganisation?.logoUrl, banner: currentOrganisation?.bannerUrl || "", bio: currentOrganisation?.bio || "", + calVideoLogo: currentOrganisation?.calVideoLogo || "", slug: currentOrganisation?.slug || ((currentOrganisation?.metadata as Prisma.JsonObject)?.requestedSlug as string) || @@ -125,7 +130,10 @@ const OrgProfileView = () => { <> {isOrgAdminOrOwner ? ( - + <> + + + ) : (
@@ -182,6 +190,7 @@ const OrgProfileForm = ({ defaultValues }: { defaultValues: FormValues }) => { bio: (res.data?.bio || "") as string, slug: defaultValues["slug"], banner: (res.data?.bannerUrl || "") as string, + calVideoLogo: (res.data?.calVideoLogo || "") as string, }); await utils.viewer.teams.get.invalidate(); await utils.viewer.organizations.listCurrent.invalidate(); @@ -206,6 +215,7 @@ const OrgProfileForm = ({ defaultValues }: { defaultValues: FormValues }) => { slug: values.slug, bio: values.bio, banner: values.banner, + calVideoLogo: values.calVideoLogo, }; mutation.mutate(variables); @@ -288,6 +298,44 @@ const OrgProfileForm = ({ defaultValues }: { defaultValues: FormValues }) => { }} />
+
+ { + const showRemoveLogoButton = !!value; + return ( + <> + } + size="lg" + /> +
+
+ + {showRemoveLogoButton && ( + + )} +
+
+ + ); + }} + /> +
{

{t("org_description")}

- diff --git a/packages/features/settings/layouts/SettingsLayout.tsx b/packages/features/settings/layouts/SettingsLayout.tsx index ff3cc0d9abb453..c4f72eece6ec42 100644 --- a/packages/features/settings/layouts/SettingsLayout.tsx +++ b/packages/features/settings/layouts/SettingsLayout.tsx @@ -84,10 +84,6 @@ const tabs: VerticalTabItemProps[] = [ name: "privacy", href: "/settings/organizations/privacy", }, - { - name: "appearance", - href: "/settings/organizations/appearance", - }, { name: "billing", href: "/settings/organizations/billing", @@ -148,7 +144,7 @@ tabs.find((tab) => { // The following keys are assigned to admin only const adminRequiredKeys = ["admin"]; const organizationRequiredKeys = ["organization"]; -const organizationAdminKeys = ["privacy", "appearance", "billing", "OAuth Clients", "SSO", "directory_sync"]; +const organizationAdminKeys = ["privacy", "billing", "OAuth Clients", "SSO", "directory_sync"]; const useTabs = () => { const session = useSession(); @@ -161,6 +157,9 @@ const useTabs = () => { const processTabsMemod = useMemo(() => { const processedTabs = tabs.map((tab) => { if (tab.href === "/settings/my-account") { + if (!!session.data?.user?.org?.id) { + tab.children = tab?.children?.filter((child) => child.href !== "/settings/my-account/appearance"); + } return { ...tab, name: user?.name || "my_account", diff --git a/packages/ui/components/image-uploader/ImageUploader.tsx b/packages/ui/components/image-uploader/ImageUploader.tsx index f9abc562f31818..2c615e0f245999 100644 --- a/packages/ui/components/image-uploader/ImageUploader.tsx +++ b/packages/ui/components/image-uploader/ImageUploader.tsx @@ -21,6 +21,7 @@ type ImageUploaderProps = { triggerButtonColor?: ButtonColor; uploadInstruction?: string; disabled?: boolean; + testId?: string; }; // This is separate to prevent loading the component until file upload @@ -73,6 +74,7 @@ export default function ImageUploader({ imageSrc, uploadInstruction, disabled = false, + testId, }: ImageUploaderProps) { const { t } = useLocale(); const [croppedAreaPixels, setCroppedAreaPixels] = useState(null); @@ -125,7 +127,7 @@ export default function ImageUploader({ color={triggerButtonColor ?? "secondary"} type="button" disabled={disabled} - data-testid="open-upload-avatar-dialog" + data-testid={testId ? `open-upload-${testId}-dialog` : "open-upload-avatar-dialog"} className="cursor-pointer py-1 text-sm"> {buttonMsg} @@ -147,7 +149,7 @@ export default function ImageUploader({ )} {result && }