This commit is contained in:
Alan 2023-09-04 13:14:36 -07:00
parent b48934b126
commit 247df0c69e
2 changed files with 62 additions and 84 deletions

View File

@ -11,9 +11,10 @@ import { z } from "zod";
import { getLayout } from "@calcom/features/MainLayout"; import { getLayout } from "@calcom/features/MainLayout";
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider"; import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
import useIntercom from "@calcom/features/ee/support/lib/intercom/useIntercom"; import useIntercom from "@calcom/features/ee/support/lib/intercom/useIntercom";
import { EventTypeEmbedButton } from "@calcom/features/embed/EventTypeEmbed"; import { EventTypeEmbedButton, EventTypeEmbedDialog } from "@calcom/features/embed/EventTypeEmbed";
import { EventTypeDescriptionLazy as EventTypeDescription } from "@calcom/features/eventtypes/components"; import { EventTypeDescriptionLazy as EventTypeDescription } from "@calcom/features/eventtypes/components";
import CreateEventTypeDialog from "@calcom/features/eventtypes/components/CreateEventTypeDialog"; import CreateEventTypeDialog from "@calcom/features/eventtypes/components/CreateEventTypeDialog";
import { DuplicateDialog } from "@calcom/features/eventtypes/components/DuplicateDialog";
import { TeamsFilter } from "@calcom/features/filters/components/TeamsFilter"; import { TeamsFilter } from "@calcom/features/filters/components/TeamsFilter";
import { getTeamsFiltersFromQuery } from "@calcom/features/filters/lib/getTeamsFiltersFromQuery"; import { getTeamsFiltersFromQuery } from "@calcom/features/filters/lib/getTeamsFiltersFromQuery";
import { ShellMain } from "@calcom/features/shell/Shell"; import { ShellMain } from "@calcom/features/shell/Shell";
@ -74,17 +75,15 @@ import {
import useMeQuery from "@lib/hooks/useMeQuery"; import useMeQuery from "@lib/hooks/useMeQuery";
import PageWrapper from "@components/PageWrapper"; import PageWrapper from "@components/PageWrapper";
import SkeletonLoader from "@components/eventtype/SkeletonLoader";
type EventTypeGroups = RouterOutputs["viewer"]["eventTypes"]["getByViewer"]["eventTypeGroups"]; type EventTypeGroups = RouterOutputs["viewer"]["eventTypes"]["getByViewer"]["eventTypeGroups"];
type EventTypeGroupProfile = EventTypeGroups[number]["profile"]; type EventTypeGroupProfile = EventTypeGroups[number]["profile"];
type PaginateEventTypeViewer = RouterOutputs["viewer"]["eventTypes"]["paginate"]; type PaginateEventTypeViewer = RouterOutputs["viewer"]["eventTypes"]["paginate"];
interface EventTypeListHeadingProps { interface EventTypeListHeadingProps {
profile: { teamSlugOrUsername: string;
name: string | null; teamNameOrUserName: string;
slug?: string | null;
username?: string | null;
};
membershipCount: number; membershipCount: number;
teamId?: number | null; teamId?: number | null;
orgSlug?: string; orgSlug?: string;
@ -698,7 +697,8 @@ export const EventTypeList = ({ data }: EventTypeListProps): JSX.Element => {
}; };
const EventTypeListHeading = ({ const EventTypeListHeading = ({
profile, teamSlugOrUsername,
teamNameOrUserName,
membershipCount, membershipCount,
teamId, teamId,
}: EventTypeListHeadingProps): JSX.Element => { }: EventTypeListHeadingProps): JSX.Element => {
@ -707,7 +707,7 @@ const EventTypeListHeading = ({
const orgBranding = useOrgBranding(); const orgBranding = useOrgBranding();
const publishTeamMutation = trpc.viewer.teams.publish.useMutation({ const publishTeamMutation = trpc.viewer.teams.publish.useMutation({
onSuccess(data) { onSuccess(data: { url: string }) {
router.push(data.url); router.push(data.url);
}, },
onError: (error) => { onError: (error) => {
@ -715,11 +715,11 @@ const EventTypeListHeading = ({
}, },
}); });
const bookerUrl = useBookerUrl(); const bookerUrl = useBookerUrl();
const slug = profile?.slug || profile?.username; const slug = teamSlugOrUsername;
return ( return (
<div className="mb-4 flex items-center space-x-2"> <div className="mb-4 flex items-center space-x-2">
<Avatar <Avatar
alt={profile?.name || ""} alt={teamNameOrUserName}
href={teamId ? `/settings/teams/${teamId}/profile` : "/settings/my-account/profile"} href={teamId ? `/settings/teams/${teamId}/profile` : "/settings/my-account/profile"}
imageSrc={`${orgBranding?.fullDomain ?? WEBAPP_URL}/${slug}/avatar.png` || undefined} imageSrc={`${orgBranding?.fullDomain ?? WEBAPP_URL}/${slug}/avatar.png` || undefined}
size="md" size="md"
@ -729,7 +729,7 @@ const EventTypeListHeading = ({
<Link <Link
href={teamId ? `/settings/teams/${teamId}/profile` : "/settings/my-account/profile"} href={teamId ? `/settings/teams/${teamId}/profile` : "/settings/my-account/profile"}
className="text-emphasis font-bold"> className="text-emphasis font-bold">
{profile?.name || ""} {teamNameOrUserName}
</Link> </Link>
{membershipCount >= 0 && teamId && ( {membershipCount >= 0 && teamId && (
<span className="text-subtle relative -top-px me-2 ms-2 text-xs"> <span className="text-subtle relative -top-px me-2 ms-2 text-xs">
@ -827,15 +827,24 @@ const SetupProfileBanner = ({ closeAction }: { closeAction: () => void }) => {
); );
}; };
const EmptyEventTypeList = ({ group }: { group: EventTypeGroup }) => { const EmptyEventTypeList = ({
teamSlugOrUsername,
teamId,
}: {
teamSlugOrUsername: string | null;
teamId?: number | null;
}) => {
const { t } = useLocale(); const { t } = useLocale();
if (!teamSlugOrUsername && !teamId) {
return null;
}
return ( return (
<> <>
<EmptyScreen <EmptyScreen
headline={t("team_no_event_types")} headline={t("team_no_event_types")}
buttonRaw={ buttonRaw={
<Button <Button
href={`?dialog=new&eventPage=${group.profile.slug}&teamId=${group.teamId}`} href={`?dialog=new&eventPage=${teamSlugOrUsername}${teamId ? `&teamId=${teamId}` : ""}`}
variant="button" variant="button"
className="mt-5"> className="mt-5">
{t("create")} {t("create")}
@ -851,14 +860,6 @@ const Main = ({ filters }: { filters: ReturnType<typeof getTeamsFiltersFromQuery
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const orgBranding = useOrgBranding(); const orgBranding = useOrgBranding();
// if (!data || status === "loading") {
// return <SkeletonLoader />;
// }
// if (status === "error") {
// return <Alert severity="error" title="Something went wrong" message={errorMessage} />;
// }
// const isFilteredByOnlyOneItem = // const isFilteredByOnlyOneItem =
// (filters?.teamIds?.length === 1 || filters?.userIds?.length === 1) && data?.eventTypeGroups.length === 1; // (filters?.teamIds?.length === 1 || filters?.userIds?.length === 1) && data?.eventTypeGroups.length === 1;
const isFilteredByOnlyOneItem = false; const isFilteredByOnlyOneItem = false;
@ -882,14 +883,23 @@ const Main = ({ filters }: { filters: ReturnType<typeof getTeamsFiltersFromQuery
refetchOnWindowFocus: false, refetchOnWindowFocus: false,
}); });
console.log({ teams });
// After teams are fetched we then load event types for each team // After teams are fetched we then load event types for each team
const eventTypePaginate = trpc.useQueries( const eventTypePaginate = trpc.useQueries(
(t) => (t) =>
teams?.map((team) => t.viewer.eventTypes.paginate({ page: 1, pageSize: 10, teamIds: [team.id] }), { teams?.map((team) => t.viewer.eventTypes.paginate({ page: 1, pageSize: 10, teamIds: [team.id] }), {
enabled: !!teams, enabled: teams.length > 0,
}) || [] }) || []
); );
if (status === "error") {
return <Alert severity="error" title="Something went wrong" message={error.message} />;
}
if (!data || status === "loading") {
return <SkeletonLoader />;
}
if ((!!data && data.length > 1) || isFilteredByOnlyOneItem) { if ((!!data && data.length > 1) || isFilteredByOnlyOneItem) {
return ( return (
<> <>
@ -898,46 +908,59 @@ const Main = ({ filters }: { filters: ReturnType<typeof getTeamsFiltersFromQuery
) : ( ) : (
<div className="mt-4 flex flex-col"> <div className="mt-4 flex flex-col">
<EventTypeListHeading <EventTypeListHeading
profile={data[0].users[0] || data[0].team} teamSlugOrUsername={data[0].users[0].username || ""}
teamNameOrUserName={data[0].users[0].name || ""}
membershipCount={data[0].team?.members.length || 0} membershipCount={data[0].team?.members.length || 0}
teamId={data[0].teamId} teamId={data[0].teamId}
orgSlug={orgBranding?.slug} orgSlug={orgBranding?.slug}
/> />
{data[0].length > 0 ? ( {data[0].length > 0 ? (
<EventTypeList data={data} readOnly={false} /> <EventTypeList data={data} />
) : ( ) : (
<EmptyEventTypeList group={group} /> <EmptyEventTypeList teamSlugOrUsername={data[0].users[0].username ?? ""} />
)} )}
{/* Then we list team event types */} {/* Then we list team event types */}
{eventTypePaginate.map((trpcFetch, index) => { {eventTypePaginate.map((trpcFetch, index) => {
const { data, status, error } = trpcFetch; const { data, status } = trpcFetch;
const [eventTypes] = data || []; const [eventTypes] = data || [];
if ((status !== "success" && !!data) || data?.length === 0 || !eventTypes) { if ((status !== "success" && !!data) || data?.length === 0 || !eventTypes) {
return null; return null;
} }
console.log("eventTypes", eventTypes);
// Type safety
if (!!!eventTypes.team) {
return null;
}
return ( return (
<> <>
<EventTypeListHeading <EventTypeListHeading
profile={eventTypes.users[0] || eventTypes.team} key={index}
teamSlugOrUsername={eventTypes.team.slug || ""}
teamNameOrUserName={eventTypes.team.name || ""}
membershipCount={eventTypes.team?.members.length || 0} membershipCount={eventTypes.team?.members.length || 0}
teamId={eventTypes.teamId} teamId={eventTypes.teamId}
orgSlug={orgBranding?.slug} orgSlug={orgBranding?.slug}
/> />
{eventTypes.length > 0 ? ( {eventTypes.length > 0 ? (
<EventTypeList data={data} readOnly={false} /> <EventTypeList data={data} readOnly={false} key={index} />
) : ( ) : (
<EmptyEventTypeList group={group} /> <EmptyEventTypeList
teamId={eventTypes.teamId}
teamSlugOrUsername={eventTypes.team.slug || ""}
key={index}
/>
)} )}
</> </>
); );
})} })}
</div> </div>
)} )}
<EventTypeEmbedDialog />
{searchParams?.get("dialog") === "duplicate" && <DuplicateDialog />}
</> </>
); );
} else if (!!data && data.length === 1) { } else if (!!data && data.length === 1) {
@ -945,11 +968,6 @@ const Main = ({ filters }: { filters: ReturnType<typeof getTeamsFiltersFromQuery
} else if (!!data && data.length === 0) { } else if (!!data && data.length === 0) {
return <CreateFirstEventTypeView />; return <CreateFirstEventTypeView />;
} }
// <EventTypeEmbedDialog />;
// {
// searchParams?.get("dialog") === "duplicate" && <DuplicateDialog />;
// }
}; };
const EventTypesPage = () => { const EventTypesPage = () => {
@ -962,40 +980,6 @@ const EventTypesPage = () => {
const routerQuery = useRouterQuery(); const routerQuery = useRouterQuery();
const filters = getTeamsFiltersFromQuery(routerQuery); const filters = getTeamsFiltersFromQuery(routerQuery);
// TODO: Maybe useSuspenseQuery to focus on success case only? Remember that it would crash the page when there is an error in query. Also, it won't support skeleton
// const { data, status, error } = trpc.viewer.eventTypes.getByViewer.useQuery(filters && { filters }, {
// refetchOnWindowFocus: false,
// cacheTime: 1 * 60 * 60 * 1000,
// staleTime: 1 * 60 * 60 * 1000,
// });
// // We first load user event types and then team event types
// const { data, status, error } = trpc.viewer.eventTypes.paginate.useQuery(
// {
// page: 1,
// pageSize: 10,
// },
// {
// trpc: {
// context: { skipBatch: true },
// },
// }
// );
// const { data: teams } = trpc.viewer.teams.list.useQuery(undefined, {
// // Teams don't change that frequently
// refetchOnWindowFocus: false,
// });
// const eventTypePaginate = trpc.useQueries(
// (t) =>
// teams?.map((team) => t.viewer.eventTypes.paginate({ page: 1, pageSize: 10, teamIds: [team.id] }), {
// enabled: !!teams,
// trpc: {
// context: { skipBatch: true },
// },
// }) || []
// );
function closeBanner() { function closeBanner() {
setShowProfileBanner(false); setShowProfileBanner(false);
document.cookie = `calcom-profile-banner=1;max-age=${60 * 60 * 24 * 90}`; // 3 months document.cookie = `calcom-profile-banner=1;max-age=${60 * 60 * 24 * 90}`; // 3 months

View File

@ -26,31 +26,24 @@ export const paginateHandler = async ({ ctx, input }: EventTypesPaginateProps) =
} }
let teamConditional: Prisma.EventTypeWhereInput = {}; let teamConditional: Prisma.EventTypeWhereInput = {};
const whereConditional: Prisma.EventTypeWhereInput = {
userId,
};
if (teamIds && teamIds.length === 1) { if (teamIds && teamIds.length === 1) {
whereConditional.userId = null;
teamConditional = { teamId: teamIds[0] }; teamConditional = { teamId: teamIds[0] };
} else if (teamIds && teamIds.length > 1) { } else if (teamIds && teamIds.length > 1) {
teamConditional = { teamId: { in: teamIds } }; teamConditional = { teamId: { in: teamIds } };
} }
const whereConditional: Prisma.EventTypeWhereInput = {
userId,
...teamConditional,
};
const skip = (page - 1) * pageSize; const skip = (page - 1) * pageSize;
// const selectWithTeam: Prisma.EventTypeSelect = {
// team: {
// select: {
// id: true,
// name: true,
// },
// },
// };
const result = await prisma.eventType.findMany({ const result = await prisma.eventType.findMany({
where: { where: {
...whereConditional, ...whereConditional,
...teamConditional,
}, },
select: { select: {
id: true, id: true,
@ -73,6 +66,7 @@ export const paginateHandler = async ({ ctx, input }: EventTypesPaginateProps) =
select: { select: {
id: true, id: true,
slug: true, slug: true,
name: true,
members: { members: {
select: { select: {
userId: true, userId: true,