Files
calendar/apps/web/components/apps/installation/AccountsStepCard.tsx
T
853f9bc436 perf: do not import from @calcom/ui barrel file (#20184)
* Icon and IconName

* Button and ButtonGroup

* UserAvatar

* AvatarGroup

* Avatar

* WizardLayout

* Dialogs

* EmptyScreen

* showToast and TextField

* Editor

* Skeleton

* Skeleton

* TopBanner and showToast

* Button again

* more

* perf: Remove app-store reference from @calcom/ui

* more

* Fixing types

* Icon

* Fixed casing

* dropdown

* more

* Select

* more

* Badge

* List

* more

* Divider

* more

* fix

* fix type check

* refactor

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix type check

* fix

* fix

* fix

* fix

* more

* more

* more

* more

* add index file to components/command

* fix

* fix

* fix

* fix imports

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix build errors

* fix build errors

* fix

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2025-03-19 19:00:55 -03:00

105 lines
3.0 KiB
TypeScript

import type { FC } from "react";
import React, { useState } from "react";
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { User } from "@calcom/prisma/client";
import classNames from "@calcom/ui/classNames";
import { Avatar } from "@calcom/ui/components/avatar";
import { StepCard } from "@calcom/ui/components/card";
import type { TTeams } from "~/apps/installation/[[...step]]/step-view";
export type PersonalAccountProps = Pick<User, "id" | "avatarUrl" | "name"> & { alreadyInstalled: boolean };
type AccountStepCardProps = {
teams?: TTeams;
personalAccount: PersonalAccountProps;
onSelect: (id?: number) => void;
loading: boolean;
installableOnTeams: boolean;
};
type AccountSelectorProps = {
avatar?: string;
name: string;
alreadyInstalled: boolean;
onClick?: () => void;
loading: boolean;
testId: string;
};
const AccountSelector: FC<AccountSelectorProps> = ({
avatar,
alreadyInstalled,
name,
onClick,
loading,
testId,
}) => {
const { t } = useLocale();
const [selected, setSelected] = useState(false);
return (
<div
className={classNames(
"hover:bg-muted flex cursor-pointer flex-row items-center gap-2 p-1",
(alreadyInstalled || loading) && "cursor-not-allowed",
selected && loading && "bg-muted animate-pulse"
)}
data-testid={testId}
onClick={() => {
if (!alreadyInstalled && !loading && onClick) {
setSelected(true);
onClick();
}
}}>
<Avatar
alt={avatar || ""}
imageSrc={getPlaceholderAvatar(avatar, name)} // if no image, use default avatar
size="sm"
/>
<div className="text-md text-subtle pt-0.5 font-medium">
{name}
{alreadyInstalled ? <span className="text-subtle ml-2 text-sm">({t("already_installed")})</span> : ""}
</div>
</div>
);
};
export const AccountsStepCard: FC<AccountStepCardProps> = ({
teams,
personalAccount,
onSelect,
loading,
installableOnTeams,
}) => {
const { t } = useLocale();
return (
<StepCard>
<div className="text-subtle text-sm font-medium">{t("install_app_on")}</div>
<div className={classNames("mt-2 flex flex-col gap-2 ")}>
<AccountSelector
testId="install-app-button-personal"
avatar={personalAccount.avatarUrl ?? ""}
name={personalAccount.name ?? ""}
alreadyInstalled={personalAccount.alreadyInstalled}
onClick={() => onSelect()}
loading={loading}
/>
{installableOnTeams &&
teams?.map((team) => (
<AccountSelector
key={team.id}
testId={`install-app-button-team${team.id}`}
alreadyInstalled={team.alreadyInstalled}
avatar={team.logoUrl ?? ""}
name={team.name}
onClick={() => onSelect(team.id)}
loading={loading}
/>
))}
</div>
</StepCard>
);
};