Skip to content

Commit

Permalink
Merge branch 'calcom:main' into feat/twipla-app
Browse files Browse the repository at this point in the history
  • Loading branch information
vikaspatil0021 committed Apr 28, 2024
2 parents 3f243a2 + 4a8f251 commit 0ba1010
Show file tree
Hide file tree
Showing 19 changed files with 184 additions and 50 deletions.
11 changes: 0 additions & 11 deletions apps/api/v2/next-i18next.config.js

This file was deleted.

2 changes: 1 addition & 1 deletion apps/api/v2/src/app.ts
Expand Up @@ -4,7 +4,7 @@ import { PrismaExceptionFilter } from "@/filters/prisma-exception.filter";
import { SentryFilter } from "@/filters/sentry-exception.filter";
import { ZodExceptionFilter } from "@/filters/zod-exception.filter";
import type { ValidationError } from "@nestjs/common";
import { BadRequestException, RequestMethod, ValidationPipe, VersioningType } from "@nestjs/common";
import { BadRequestException, ValidationPipe, VersioningType } from "@nestjs/common";
import { HttpAdapterHost } from "@nestjs/core";
import type { NestExpressApplication } from "@nestjs/platform-express";
import * as Sentry from "@sentry/node";
Expand Down
2 changes: 1 addition & 1 deletion apps/api/v2/src/ee/event-types/event-types.repository.ts
@@ -1,11 +1,11 @@
import { CreateEventTypeInput } from "@/ee/event-types/inputs/create-event-type.input";
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import type { PrismaClient } from "@calcom/prisma";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { UserWithProfile } from "@/modules/users/users.repository";
import { Injectable } from "@nestjs/common";

import { getEventTypeById } from "@calcom/platform-libraries";
import type { PrismaClient } from "@calcom/prisma";

@Injectable()
export class EventTypesRepository {
Expand Down
13 changes: 11 additions & 2 deletions apps/api/v2/tsconfig.json
Expand Up @@ -14,7 +14,11 @@
"jsx": "react-jsx",
"paths": {
"@/*": ["./src/*"],
"@prisma/client/*": ["@calcom/prisma/client/*"]
"@prisma/client/*": ["@calcom/prisma/client/*"],
"@calcom/platform-libraries": ["../../../packages/platform/libraries/index.ts"],
"@calcom/platform-constants": ["../../../packages/platform/constants/index.ts"],
"@calcom/platform-types": ["../../../packages/platform/types/index.ts"],
"@calcom/platform-utils": ["../../../packages/platform/utils/index.ts"]
},
"incremental": true,
"skipLibCheck": true,
Expand All @@ -24,5 +28,10 @@
"noFallthroughCasesInSwitch": false
},
"exclude": ["./dist", "next-i18next.config.js"],
"include": ["./**/*.ts", "../../../packages/types/*.d.ts"]
"include": [
"./**/*.ts",
"../../../packages/types/*.d.ts",
"../../../packages/platform/**/*.ts",
"../../../packages/platform/**/*.d.ts"
]
}
3 changes: 2 additions & 1 deletion apps/web/lib/team/[slug]/getServerSideProps.tsx
Expand Up @@ -2,6 +2,7 @@ import type { GetServerSidePropsContext } from "next";

import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import { getFeatureFlag } from "@calcom/features/flags/server/utils";
import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl";
import { getBookerBaseUrlSync } from "@calcom/lib/getBookerUrl/client";
import logger from "@calcom/lib/logger";
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
Expand Down Expand Up @@ -127,7 +128,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
users: !isTeamOrParentOrgPrivate
? type.users.map((user) => ({
...user,
avatar: `/${user.username}/avatar.png`,
avatar: getUserAvatarUrl(user),
}))
: [],
descriptionAsSafeHTML: markdownToSafeHTML(type.description),
Expand Down
4 changes: 2 additions & 2 deletions apps/web/pages/team/[slug].tsx
Expand Up @@ -13,7 +13,7 @@ import { useEffect } from "react";

import { sdkActionManager, useIsEmbed } from "@calcom/embed-core/embed-iframe";
import EventTypeDescription from "@calcom/features/eventtypes/components/EventTypeDescription";
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
import { getOrgOrTeamAvatar } from "@calcom/lib/defaultAvatarImage";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
import useTheme from "@calcom/lib/hooks/useTheme";
Expand Down Expand Up @@ -162,7 +162,7 @@ function TeamPage({
</div>
);

const profileImageSrc = getPlaceholderAvatar(team.logoUrl || team.parent?.logoUrl, team.name);
const profileImageSrc = getOrgOrTeamAvatar(team);

return (
<>
Expand Down
2 changes: 2 additions & 0 deletions apps/web/public/static/locales/fr/common.json
Expand Up @@ -324,7 +324,9 @@
"other": "Autre",
"email_sign_in_subject": "Votre lien de connexion pour {{appName}}",
"emailed_you_and_attendees": "Nous avons envoyé un e-mail avec une invitation de calendrier comprenant les détails à tout le monde.",
"round_robin_emailed_you_and_attendees": "Vous avez rendez-vous avec {{user}}. Nous avons envoyé un e-mail avec une invitation de calendrier comprenant les détails à tout le monde.",
"emailed_you_and_attendees_recurring": "Nous avons envoyé un e-mail avec une invitation de calendrier comprenant les détails à tout le monde pour le premier de ces événements récurrents.",
"round_robin_emailed_you_and_attendees_recurring": "Vous avez rendez-vous avec {{user}}. Nous avons envoyé un e-mail avec une invitation de calendrier comprenant les détails à tout le monde pour le premier de ces événements récurrents.",
"emailed_you_and_any_other_attendees": "Nous avons envoyé cette information à tout le monde par e-mail.",
"needs_to_be_confirmed_or_rejected": "Votre réservation doit encore être confirmée ou refusée.",
"needs_to_be_confirmed_or_rejected_recurring": "Votre rendez-vous récurrent doit encore être confirmé ou refusé.",
Expand Down
1 change: 1 addition & 0 deletions packages/embeds/embed-react/playwright/tests/basic.e2e.ts
@@ -1,6 +1,7 @@
import { expect } from "@playwright/test";

import { getEmbedIframe } from "@calcom/embed-core/playwright/lib/testUtils";
// eslint-disable-next-line no-restricted-imports
import { test } from "@calcom/web/playwright/lib/fixtures";

test.describe("React Embed", () => {
Expand Down
1 change: 1 addition & 0 deletions packages/eslint-plugin/src/configs/recommended.ts
Expand Up @@ -6,6 +6,7 @@ const recommended = {
"@calcom/eslint/deprecated-imports-next-router": "error",
"@calcom/eslint/avoid-web-storage": "error",
"@calcom/eslint/avoid-prisma-client-import-for-enums": "error",
"@calcom/eslint/no-prisma-include-true": "warn",
},
};

Expand Down
1 change: 1 addition & 0 deletions packages/eslint-plugin/src/rules/index.ts
Expand Up @@ -5,5 +5,6 @@ export default {
"deprecated-imports": require("./deprecated-imports").default,
"avoid-web-storage": require("./avoid-web-storage").default,
"avoid-prisma-client-import-for-enums": require("./avoid-prisma-client-import-for-enums").default,
"no-prisma-include-true": require("./no-prisma-include-true").default,
"deprecated-imports-next-router": require("./deprecated-imports-next-router").default,
} as ESLint.Plugin["rules"];
100 changes: 100 additions & 0 deletions packages/eslint-plugin/src/rules/no-prisma-include-true.ts
@@ -0,0 +1,100 @@
import type { TSESTree } from "@typescript-eslint/utils";
import { ESLintUtils } from "@typescript-eslint/utils";
import type { ReportDescriptor } from "@typescript-eslint/utils/dist/ts-eslint";

const createRule = ESLintUtils.RuleCreator((name) => `https://developer.cal.com/eslint/rule/${name}`);

const assesIncludePropertyIncludesTrue = (
includeProperty: TSESTree.Property,
reporter: { (reportObj: ReportDescriptor<"no-prisma-include-true">): void }
) => {
if (includeProperty.value.type === "ObjectExpression") {
includeProperty.value.properties.forEach((childProperty) => {
if (
childProperty.type === "Property" &&
childProperty.value.type === "Literal" &&
childProperty.value.value === true
) {
reporter({
node: childProperty,
messageId: "no-prisma-include-true",
});
}
});
}
};

const searchIncludeProperty = (
property: TSESTree.Property,
reporter: { (reportObj: ReportDescriptor<"no-prisma-include-true">): void }
) => {
if (property.type === "Property") {
// If property is include, check if it has a child property with value true
if (property.key.type === "Identifier" && property.key.name === "include") {
assesIncludePropertyIncludesTrue(property, reporter);
}

// If property value is also an object, recursively search for include property
if (property.value.type === "ObjectExpression") {
property.value.properties.forEach((childProperty) => {
if (childProperty.type === "Property") {
searchIncludeProperty(childProperty, reporter);
}
});
}
}
};

const rule = createRule({
create: function (context) {
return {
CallExpression(node) {
if (!(node.callee as TSESTree.MemberExpression).property) {
return null;
}

const nodeName = ((node.callee as TSESTree.MemberExpression).property as TSESTree.Identifier).name;

if (
!["findUnique", "findUniqueOrThrow", "findFirst", "findFirstOrThrow", "findMany"].includes(nodeName)
) {
return null;
}

const nodeArgs = node.arguments[0] as TSESTree.ObjectExpression;
if (!nodeArgs) {
return null;
}

const backReporter = (reportObj: ReportDescriptor<"no-prisma-include-true">) => {
context.report(reportObj);
};

nodeArgs.properties?.forEach((property) => {
if (property.type === "Property") {
searchIncludeProperty(property, backReporter);
}
});
return null;
},
};
},

name: "no-prisma-include-true",
meta: {
type: "problem",
docs: {
description:
"Disallow passing argument object with include: { AnyPropertyName: true } to prisma methods",
recommended: "error",
},
messages: {
"no-prisma-include-true": `Do not pass argument object with include: { AnyPropertyName: true } to prisma methods`,
},
fixable: "code",
schema: [],
},
defaultOptions: [],
});

export default rule;
2 changes: 1 addition & 1 deletion packages/features/bookings/Booker/components/EventMeta.tsx
Expand Up @@ -84,7 +84,7 @@ export const EventMeta = ({
: "text-bookinghighlight";

return (
<div className={`${classNames?.eventMetaContainer} relative z-10 p-6`} data-testid="event-meta">
<div className={`${classNames?.eventMetaContainer || ""} relative z-10 p-6`} data-testid="event-meta">
{isPending && (
<m.div {...fadeInUp} initial="visible" layout>
<EventMetaSkeleton />
Expand Down
42 changes: 27 additions & 15 deletions packages/features/bookings/components/event-meta/Members.tsx
@@ -1,6 +1,9 @@
import { useIsPlatform } from "@calcom/atoms/monorepo";
import { useIsEmbed } from "@calcom/embed-core/embed-iframe";
import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl";
import { getBookerBaseUrlSync } from "@calcom/lib/getBookerUrl/client";
import { SchedulingType } from "@calcom/prisma/enums";
import { UserAvatarGroup, UserAvatarGroupWithOrg } from "@calcom/ui";
import { AvatarGroup } from "@calcom/ui";

import type { PublicEvent } from "../../types";

Expand All @@ -17,7 +20,8 @@ export interface EventMembersProps {

export const EventMembers = ({ schedulingType, users, profile, entity }: EventMembersProps) => {
const isPlatform = useIsPlatform();
const showMembers = schedulingType !== SchedulingType.ROUND_ROBIN;
const isEmbed = useIsEmbed();
const showMembers = !!schedulingType && schedulingType !== SchedulingType.ROUND_ROBIN;
const shownUsers = showMembers && !isPlatform ? users : [];

// In some cases we don't show the user's names, but only show the profile name.
Expand All @@ -28,19 +32,27 @@ export const EventMembers = ({ schedulingType, users, profile, entity }: EventMe

return (
<>
{entity.orgSlug ? (
<UserAvatarGroupWithOrg
size="sm"
className="border-muted"
organization={{
slug: entity.orgSlug,
name: entity.name || "",
}}
users={shownUsers}
/>
) : (
<UserAvatarGroup size="sm" className="border-muted" users={shownUsers} />
)}
<AvatarGroup
size="sm"
className="border-muted"
items={[
{
// We don't want booker to be able to see the list of other users or teams inside the embed
href: isEmbed ? null : getBookerBaseUrlSync(entity.orgSlug),
image: profile.image || "",
alt: profile.name || "",
title: profile.name || "",
},
...shownUsers.map((user) => ({
href: `${getBookerBaseUrlSync(user.profile?.organization?.slug ?? null)}/${
user.profile?.username
}?redirect=false`,
alt: user.name || "",
title: user.name || "",
image: getUserAvatarUrl(user),
})),
]}
/>

<p className="text-subtle mt-2 text-sm font-semibold">
{showOnlyProfileName
Expand Down
6 changes: 3 additions & 3 deletions packages/features/eventtypes/lib/getPublicEvent.ts
Expand Up @@ -7,7 +7,7 @@ import { getAppFromSlug } from "@calcom/app-store/utils";
import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/getBookingFields";
import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains";
import { isRecurringEvent, parseRecurringEvent } from "@calcom/lib";
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
import { getOrgOrTeamAvatar } from "@calcom/lib/defaultAvatarImage";
import { getDefaultEvent, getUsernameList } from "@calcom/lib/defaultEvents";
import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl";
import { getBookerBaseUrlSync } from "@calcom/lib/getBookerUrl/client";
Expand Down Expand Up @@ -86,6 +86,7 @@ const publicEventSelect = Prisma.validator<Prisma.EventTypeSelect>()({
slug: true,
name: true,
bannerUrl: true,
logoUrl: true,
},
},
},
Expand Down Expand Up @@ -335,11 +336,10 @@ function getProfileFromEvent(event: Event) {
name: profile.name,
weekStart,
image: team
? undefined
? getOrgOrTeamAvatar(team)
: getUserAvatarUrl({
avatarUrl: nonTeamprofile?.avatarUrl,
}),
logo: !team ? undefined : getPlaceholderAvatar(team.logoUrl, team.name),
brandColor: profile.brandColor,
darkBrandColor: profile.darkBrandColor,
theme: profile.theme,
Expand Down
10 changes: 2 additions & 8 deletions packages/features/filters/components/TeamsFilter.tsx
Expand Up @@ -3,7 +3,7 @@ import type { InputHTMLAttributes, ReactNode } from "react";
import { forwardRef } from "react";

import { classNames } from "@calcom/lib";
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
import { getOrgOrTeamAvatar } from "@calcom/lib/defaultAvatarImage";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery";
import type { RouterOutputs } from "@calcom/trpc/react";
Expand Down Expand Up @@ -111,13 +111,7 @@ export const TeamsFilter = ({
removeItemByKeyAndValue("teamIds", team.id);
}
}}
icon={
<Avatar
alt={team?.name}
imageSrc={getPlaceholderAvatar(team.logoUrl, team?.name as string)}
size="xs"
/>
}
icon={<Avatar alt={team?.name} imageSrc={getOrgOrTeamAvatar(team)} size="xs" />}
/>
))}
</FilterCheckboxFieldsContainer>
Expand Down
8 changes: 8 additions & 0 deletions packages/lib/defaultAvatarImage.ts
@@ -1,3 +1,5 @@
import type { Team } from "@calcom/prisma/client";

/**
* Given an avatar URL and a name, return the appropriate avatar URL. In the
* event that no avatar URL is provided, return a placeholder avatar URL from
Expand All @@ -14,3 +16,9 @@ export function getPlaceholderAvatar(avatar: string | null | undefined, name: st
name || ""
)}`;
}

export function getOrgOrTeamAvatar(
team: Pick<Team, "logoUrl" | "name"> & { parent?: Pick<Team, "logoUrl"> | null }
) {
return getPlaceholderAvatar(team.logoUrl || team.parent?.logoUrl, team.name);
}
10 changes: 7 additions & 3 deletions packages/lib/entityPermissionUtils.ts
Expand Up @@ -64,7 +64,12 @@ async function getMembership(teamId: number | null, userId: number) {
},
},
include: {
members: true,
members: {
select: {
userId: true,
role: true,
},
},
},
})
: null;
Expand All @@ -81,8 +86,7 @@ export async function canCreateEntity({
if (targetTeamId) {
// If it doesn't exist and it is being created for a team. Check if user is the member of the team
const membership = await getMembership(targetTeamId, userId);
const creationAllowed = membership ? withRoleCanCreateEntity(membership.role) : false;
return creationAllowed;
return membership ? withRoleCanCreateEntity(membership.role) : false;
}
return true;
}
Expand Down

0 comments on commit 0ba1010

Please sign in to comment.