"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { useRouter } from "next/navigation";
import { useEffect, useLayoutEffect, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { z } from "zod";
import LicenseRequired from "~/ee/common/components/LicenseRequired";
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
import { subdomainSuffix } from "@calcom/features/ee/organizations/lib/orgDomains";
import SectionBottomActions from "@calcom/features/settings/SectionBottomActions";
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { md } from "@calcom/lib/markdownIt";
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
import turndown from "@calcom/lib/turndownService";
import type { Prisma } from "@calcom/prisma/client";
import { trpc } from "@calcom/trpc/react";
import { Avatar } from "@calcom/ui/components/avatar";
import { Button } from "@calcom/ui/components/button";
import { LinkIconButton } from "@calcom/ui/components/button";
import { Editor } from "@calcom/ui/components/editor";
import { Form } from "@calcom/ui/components/form";
import { Label } from "@calcom/ui/components/form";
import { TextField } from "@calcom/ui/components/form";
import { BannerUploader, ImageUploader } from "@calcom/ui/components/image-uploader";
import { CopyIcon, PlusIcon } from "@coss/ui/icons";
// if I include this in the above barrel import, I get a runtime error that the component is not exported.
import { OrgBanner } from "@calcom/ui/components/organization-banner";
import {
SkeletonButton,
SkeletonContainer,
SkeletonText,
SkeletonAvatar,
} from "@calcom/ui/components/skeleton";
import { showToast } from "@calcom/ui/components/toast";
import { Tooltip } from "@calcom/ui/components/tooltip";
import OrgAppearanceViewWrapper from "./appearance";
const orgProfileFormSchema = z.object({
name: z.string(),
logoUrl: z.string().nullable(),
banner: z.string().nullable(),
calVideoLogo: z.string().nullable(),
bio: z.string(),
});
type FormValues = {
id: number;
name: string;
logoUrl: string | null;
banner: string | null;
bio: string;
slug: string;
calVideoLogo: string | null;
};
const SkeletonLoader = () => {
return (
);
};
const OrgProfileView = ({
permissions,
}: {
permissions?: {
canRead: boolean;
canEdit: boolean;
canDelete: boolean;
};
}) => {
const { t } = useLocale();
const router = useRouter();
const orgBranding = useOrgBranding();
useLayoutEffect(() => {
document.body.focus();
}, []);
const {
data: currentOrganisation,
isPending,
error,
} = trpc.viewer.organizations.listCurrent.useQuery(undefined, {});
useEffect(
function refactorMeWithoutEffect() {
if (error) {
router.replace("/enterprise");
}
},
[error, router]
);
// Scroll to top when page loads
useEffect(() => {
if (!isPending && orgBranding && currentOrganisation) {
window.scrollTo(0, 0);
}
}, [isPending, orgBranding, currentOrganisation]);
if (isPending || !orgBranding || !currentOrganisation) {
return ;
}
const isBioEmpty =
!currentOrganisation ||
!currentOrganisation.bio ||
!currentOrganisation.bio.replace("
", "").length;
const defaultValues: FormValues = {
id: currentOrganisation?.id,
name: currentOrganisation?.name || "",
logoUrl: currentOrganisation?.logoUrl,
banner: currentOrganisation?.bannerUrl || "",
bio: currentOrganisation?.bio || "",
calVideoLogo: currentOrganisation?.calVideoLogo || "",
slug:
currentOrganisation?.slug ||
((currentOrganisation?.metadata as Prisma.JsonObject)?.requestedSlug as string) ||
"",
};
return (
<>
{permissions?.canEdit ? (
<>
>
) : (
{currentOrganisation?.name}
{!isBioEmpty && (
<>
{/* biome-ignore lint/security/noDangerouslySetInnerHtml: Content is sanitized via markdownToSafeHTML */}
>
)}
{
navigator.clipboard.writeText(orgBranding.fullDomain);
showToast("Copied to clipboard", "success");
}}>
{t("copy_link_org")}
)}
{/* LEAVE ORG should go above here ^ */}
>
);
};
const OrgProfileForm = ({ defaultValues }: { defaultValues: FormValues }) => {
const utils = trpc.useUtils();
const { t } = useLocale();
const [firstRender, setFirstRender] = useState(true);
const form = useForm({
defaultValues,
resolver: zodResolver(orgProfileFormSchema),
});
const mutation = trpc.viewer.organizations.update.useMutation({
onError: (err) => {
// Handle JSON parsing errors from body size limit exceeded
if (err.message.includes("Unexpected token") && err.message.includes("Body excee")) {
showToast(t("converted_image_size_limit_exceed"), "error");
return;
}
showToast(err.message, "error");
},
onSuccess: async (res) => {
reset({
logoUrl: res.data?.logoUrl,
name: (res.data?.name || "") as string,
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();
showToast(t("your_organization_updated_sucessfully"), "success");
},
});
const {
formState: { isSubmitting, isDirty },
reset,
} = form;
const isDisabled = isSubmitting || !isDirty;
const handleCopy = async (value: string) => {
try {
await navigator.clipboard.writeText(value);
showToast(t("organization_id_copied"), "success");
} catch (error) {
showToast(t("error_copying_to_clipboard"), "error");
}
};
return (
);
};
export default OrgProfileView;