cal.pub0.org/apps/web/components/team/TeamSettings.tsx

212 lines
7.9 KiB
TypeScript

import React, { useRef, useState } from "react";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
import { objectKeys } from "@calcom/lib/objectKeys";
import { TeamWithMembers } from "@calcom/lib/server/queries/teams";
import { trpc } from "@calcom/trpc/react";
import { Alert } from "@calcom/ui/Alert";
import Button from "@calcom/ui/Button";
import { Icon } from "@calcom/ui/Icon";
import { TextField } from "@calcom/ui/form/fields";
import ImageUploader from "@components/ImageUploader";
import SettingInputContainer from "@components/ui/SettingInputContainer";
interface Props {
team: TeamWithMembers | null | undefined;
}
export default function TeamSettings(props: Props) {
const { t } = useLocale();
const [hasErrors, setHasErrors] = useState(false);
const [errorMessage, setErrorMessage] = useState("");
const team = props.team;
const hasLogo = !!team?.logo;
const utils = trpc.useContext();
const mutation = trpc.useMutation("viewer.teams.update", {
onError: (err) => {
setHasErrors(true);
setErrorMessage(err.message);
},
async onSuccess() {
await utils.invalidateQueries(["viewer.teams.get"]);
showToast(t("your_team_updated_successfully"), "success");
setHasErrors(false);
},
});
const nameRef = useRef<HTMLInputElement>() as React.MutableRefObject<HTMLInputElement>;
const teamUrlRef = useRef<HTMLInputElement>() as React.MutableRefObject<HTMLInputElement>;
const descriptionRef = useRef<HTMLTextAreaElement>() as React.MutableRefObject<HTMLTextAreaElement>;
const hideBrandingRef = useRef<HTMLInputElement>() as React.MutableRefObject<HTMLInputElement>;
const logoRef = useRef<HTMLInputElement>() as React.MutableRefObject<HTMLInputElement>;
function updateTeamData() {
if (!team) return;
const variables = {
name: nameRef.current?.value,
slug: teamUrlRef.current?.value,
bio: descriptionRef.current?.value,
hideBranding: hideBrandingRef.current?.checked,
};
// remove unchanged variables
objectKeys(variables).forEach((key) => {
if (variables[key as keyof typeof variables] === team?.[key]) delete variables[key];
});
mutation.mutate({ id: team.id, ...variables });
}
function updateLogo(newLogo: string) {
if (!team) return;
logoRef.current.value = newLogo;
mutation.mutate({ id: team.id, logo: newLogo });
}
const removeLogo = () => updateLogo("");
return (
<div className="divide-y divide-gray-200 lg:col-span-9">
<div className="">
{hasErrors && <Alert severity="error" title={errorMessage} />}
<form
className="divide-y divide-gray-200 lg:col-span-9"
onSubmit={(e) => {
e.preventDefault();
updateTeamData();
}}>
<div className="py-6">
<div className="flex flex-col lg:flex-row">
<div className="flex-grow space-y-6">
<SettingInputContainer
Icon={Icon.FiLink}
label="Team URL"
htmlFor="team-url"
Input={
<TextField
name="" // typescript requires name but we don't want component to render name label
id="team-url"
addOnLeading={
<span className="inline-flex items-center rounded-l-sm border border-r-0 border-gray-300 bg-gray-50 px-3 text-sm text-gray-500">
{process.env.NEXT_PUBLIC_WEBSITE_URL}/team/
</span>
}
ref={teamUrlRef}
defaultValue={team?.slug as string}
/>
}
/>
<SettingInputContainer
Icon={Icon.FiHash}
label="Team Name"
htmlFor="name"
Input={
<input
ref={nameRef}
type="text"
name="name"
id="name"
placeholder={t("your_team_name")}
required
className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 text-sm"
defaultValue={team?.name as string}
/>
}
/>
<hr />
<div>
<SettingInputContainer
Icon={Icon.FiInfo}
label={t("about")}
htmlFor="about"
Input={
<>
<textarea
ref={descriptionRef}
id="about"
name="about"
rows={3}
defaultValue={team?.bio as string}
className="mt-1 block w-full rounded-sm border-gray-300 text-sm"
/>
<p className="mt-2 text-sm text-gray-500">{t("team_description")}</p>
</>
}
/>
</div>
<div>
<SettingInputContainer
Icon={Icon.FiImage}
label="Logo"
htmlFor="avatar"
Input={
<>
<div className="mt-1 flex">
<input
ref={logoRef}
type="hidden"
name="avatar"
id="avatar"
placeholder="URL"
className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 text-sm"
defaultValue={team?.logo ?? undefined}
/>
<ImageUploader
target="logo"
id="logo-upload"
buttonMsg={hasLogo ? t("edit_logo") : t("upload_a_logo")}
handleAvatarChange={updateLogo}
imageSrc={team?.logo ?? undefined}
/>
{hasLogo && (
<Button
onClick={removeLogo}
color="secondary"
type="button"
className="ml-1 py-1 text-xs">
{t("remove_logo")}
</Button>
)}
</div>
</>
}
/>
<hr className="mt-6" />
</div>
<div className="relative flex items-start">
<div className="flex h-5 items-center">
<input
id="hide-branding"
name="hide-branding"
type="checkbox"
ref={hideBrandingRef}
defaultChecked={team?.hideBranding}
className="h-4 w-4 rounded-sm border-gray-300 text-neutral-900 focus:ring-neutral-500"
/>
</div>
<div className="text-sm ltr:ml-3 rtl:mr-3">
<label htmlFor="hide-branding" className="font-medium text-gray-700">
{t("disable_cal_branding")}
</label>
<p className="text-gray-500">{t("disable_cal_branding_description")}</p>
</div>
</div>
</div>
</div>
</div>
<div className="flex justify-end py-4">
<Button type="submit" color="primary">
{t("save")}
</Button>
</div>
</form>
</div>
</div>
);
}