Merge branch 'main' into testE2E-timezone
This commit is contained in:
commit
4c9461937b
|
@ -47,3 +47,9 @@ assignees: ""
|
|||
-->
|
||||
|
||||
(Share it here.)
|
||||
|
||||
---
|
||||
##### House rules
|
||||
- If this issue has a `🚨 needs approval` label, don't start coding yet. Wait until a core member approves feature request by removing this label, then you can start coding.
|
||||
- For clarity: Non-core member issues automatically get the `🚨 needs approval` label.
|
||||
- Your feature ideas are invaluable to us! However, they undergo review to ensure alignment with the product's direction.
|
||||
|
|
|
@ -40,7 +40,7 @@ Fixes # (issue)
|
|||
|
||||
## Checklist
|
||||
|
||||
<!-- Please remove all the irrelevant bullets to your PR -->
|
||||
<!-- Remove bullet points below that don't apply to you -->
|
||||
|
||||
- I haven't read the [contributing guide](https://github.com/calcom/cal.com/blob/main/CONTRIBUTING.md)
|
||||
- My code doesn't follow the style guidelines of this project
|
||||
|
|
|
@ -2,7 +2,18 @@
|
|||
|
||||
Contributions are what makes the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
|
||||
|
||||
- Before jumping into a PR be sure to search [existing PRs](https://github.com/calcom/cal.com/pulls) or [issues](https://github.com/calcom/cal.com/issues) for an open or closed item that relates to your submission.
|
||||
## House rules
|
||||
|
||||
- Before submitting a new issue or PR, check if it already exists in [issues](https://github.com/calcom/cal.com/issues) or [PRs](https://github.com/calcom/cal.com/pulls).
|
||||
- GitHub issues: take note of the `🚨 needs approval` label.
|
||||
- **For Contributors**:
|
||||
- Feature Requests: Wait for a core member to approve and remove the `🚨 needs approval` label before you start coding or submit a PR.
|
||||
- Bugs, Security, Performance, Documentation, etc.: You can start coding immediately, even if the `🚨 needs approval` label is present. This label mainly concerns feature requests.
|
||||
- **Our Process**:
|
||||
- Issues from non-core members automatically receive the `🚨 needs approval` label.
|
||||
- We greatly value new feature ideas. To ensure consistency in the product's direction, they undergo review and approval.
|
||||
|
||||
|
||||
|
||||
## Priorities
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@ Here is what you need to be able to run Cal.com.
|
|||
|
||||
### Setup
|
||||
|
||||
1. Clone the repo into a public GitHub repository (or fork https://github.com/calcom/cal.com/fork). If you plan to distribute the code, keep the source code public to comply with [AGPLv3](https://github.com/calcom/cal.com/blob/main/LICENSE). To clone in a private repository, [acquire a commercial license](https://cal.com/sales))
|
||||
1. Clone the repo into a public GitHub repository (or fork https://github.com/calcom/cal.com/fork). If you plan to distribute the code, keep the source code public to comply with [AGPLv3](https://github.com/calcom/cal.com/blob/main/LICENSE). To clone in a private repository, [acquire a commercial license](https://cal.com/sales)
|
||||
|
||||
```sh
|
||||
git clone https://github.com/calcom/cal.com.git
|
||||
|
@ -221,7 +221,7 @@ echo 'NEXT_PUBLIC_DEBUG=1' >> .env
|
|||
|
||||
1. Copy and paste your `DATABASE_URL` from `.env` to `.env.appStore`.
|
||||
|
||||
1. Set a 32 character random string in your `.env` file for the `CALENDSO_ENCRYPTION_KEY` (You can use a command like `openssl rand -base64 24` to generate one).
|
||||
1. Set a 24 character random string in your `.env` file for the `CALENDSO_ENCRYPTION_KEY` (You can use a command like `openssl rand -base64 24` to generate one).
|
||||
1. Set up the database using the Prisma schema (found in `packages/prisma/schema.prisma`)
|
||||
|
||||
In a development environment, run:
|
||||
|
@ -597,8 +597,6 @@ Distributed under the [AGPLv3 License](https://github.com/calcom/cal.com/blob/ma
|
|||
|
||||
Special thanks to these amazing projects which help power Cal.com:
|
||||
|
||||
[<img src="https://cal.com/powered-by-vercel.svg">](https://vercel.com/?utm_source=calend-so&utm_campaign=oss)
|
||||
|
||||
- [Vercel](https://vercel.com/?utm_source=calend-so&utm_campaign=oss)
|
||||
- [Next.js](https://nextjs.org/)
|
||||
- [Day.js](https://day.js.org/)
|
||||
|
|
|
@ -316,8 +316,9 @@ async function checkPermissions(req: NextApiRequest) {
|
|||
statusCode: 401,
|
||||
message: "ADMIN required for `userId`",
|
||||
});
|
||||
/* Admin users are required to pass in a userId */
|
||||
if (isAdmin && !body.userId) throw new HttpError({ statusCode: 400, message: "`userId` required" });
|
||||
/* Admin users are required to pass in a userId or teamId */
|
||||
if (isAdmin && (!body.userId || !body.teamId))
|
||||
throw new HttpError({ statusCode: 400, message: "`userId` or `teamId` required" });
|
||||
}
|
||||
|
||||
export default defaultResponder(postHandler);
|
||||
|
|
|
@ -261,13 +261,11 @@ function BookingListItem(booking: BookingItemProps) {
|
|||
const title = booking.title;
|
||||
|
||||
const showRecordingsButtons = !!(booking.isRecorded && isPast && isConfirmed);
|
||||
const checkForRecordingsButton =
|
||||
!showRecordingsButtons && (booking.location === "integrations:daily" || booking?.location?.trim() === "");
|
||||
|
||||
const showRecordingActions: ActionType[] = [
|
||||
{
|
||||
id: checkForRecordingsButton ? "check_for_recordings" : "view_recordings",
|
||||
label: checkForRecordingsButton ? t("check_for_recordings") : t("view_recordings"),
|
||||
id: "view_recordings",
|
||||
label: t("view_recordings"),
|
||||
onClick: () => {
|
||||
setViewRecordingsDialogIsOpen(true);
|
||||
},
|
||||
|
@ -298,7 +296,7 @@ function BookingListItem(booking: BookingItemProps) {
|
|||
paymentCurrency={booking.payment[0].currency}
|
||||
/>
|
||||
)}
|
||||
{(showRecordingsButtons || checkForRecordingsButton) && (
|
||||
{showRecordingsButtons && (
|
||||
<ViewRecordingsDialog
|
||||
booking={booking}
|
||||
isOpenDialog={viewRecordingsDialogIsOpen}
|
||||
|
@ -468,9 +466,7 @@ function BookingListItem(booking: BookingItemProps) {
|
|||
</>
|
||||
) : null}
|
||||
{isPast && isPending && !isConfirmed ? <TableActions actions={bookedActions} /> : null}
|
||||
{(showRecordingsButtons || checkForRecordingsButton) && (
|
||||
<TableActions actions={showRecordingActions} />
|
||||
)}
|
||||
{showRecordingsButtons && <TableActions actions={showRecordingActions} />}
|
||||
{isCancelled && booking.rescheduled && (
|
||||
<div className="hidden h-full items-center md:flex">
|
||||
<RequestSentMessage />
|
||||
|
|
|
@ -356,9 +356,9 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
|
|||
onChange={(val) => {
|
||||
if (val) {
|
||||
locationFormMethods.setValue("locationType", val.value);
|
||||
if (val.credential) {
|
||||
locationFormMethods.setValue("credentialId", val.credential.id);
|
||||
locationFormMethods.setValue("teamName", val.credential.team?.name);
|
||||
if (!!val.credentialId) {
|
||||
locationFormMethods.setValue("credentialId", val.credentialId);
|
||||
locationFormMethods.setValue("teamName", val.teamName);
|
||||
}
|
||||
|
||||
locationFormMethods.unregister([
|
||||
|
|
|
@ -298,9 +298,21 @@ export const EventSetupTab = (
|
|||
!validLocations.find((location) => location.type === newLocationType);
|
||||
|
||||
if (canAddLocation) {
|
||||
updateLocationField(index, { type: newLocationType });
|
||||
updateLocationField(index, {
|
||||
type: newLocationType,
|
||||
...(e.credentialId && {
|
||||
credentialId: e.credentialId,
|
||||
teamName: e.teamName,
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
updateLocationField(index, { type: field.type });
|
||||
updateLocationField(index, {
|
||||
type: field.type,
|
||||
...(field.credentialId && {
|
||||
credentialId: field.credentialId,
|
||||
teamName: field.teamName,
|
||||
}),
|
||||
});
|
||||
showToast(t("location_already_exists"), "warning");
|
||||
}
|
||||
}
|
||||
|
@ -382,7 +394,13 @@ export const EventSetupTab = (
|
|||
!validLocations.find((location) => location.type === newLocationType);
|
||||
|
||||
if (canAppendLocation) {
|
||||
append({ type: newLocationType });
|
||||
append({
|
||||
type: newLocationType,
|
||||
...(e.credentialId && {
|
||||
credentialId: e.credentialId,
|
||||
teamName: e.teamName,
|
||||
}),
|
||||
});
|
||||
setSelectedNewOption(e);
|
||||
} else {
|
||||
showToast(t("location_already_exists"), "warning");
|
||||
|
|
|
@ -2,7 +2,6 @@ import type { GroupBase, Props, SingleValue } from "react-select";
|
|||
import { components } from "react-select";
|
||||
|
||||
import type { EventLocationType } from "@calcom/app-store/locations";
|
||||
import type { CredentialDataWithTeamName } from "@calcom/app-store/utils";
|
||||
import { classNames } from "@calcom/lib";
|
||||
import invertLogoOnDark from "@calcom/lib/invertLogoOnDark";
|
||||
import { Select } from "@calcom/ui";
|
||||
|
@ -13,7 +12,8 @@ export type LocationOption = {
|
|||
icon?: string;
|
||||
disabled?: boolean;
|
||||
address?: string;
|
||||
credential?: CredentialDataWithTeamName;
|
||||
credentialId?: number;
|
||||
teamName?: string;
|
||||
};
|
||||
|
||||
export type SingleValueLocationOption = SingleValue<LocationOption>;
|
||||
|
|
|
@ -88,7 +88,13 @@ export async function getLocationGroupedOptions(
|
|||
teamName: credential.team?.name,
|
||||
}))) {
|
||||
const label = `${app.locationOption.label} ${teamName ? `(${teamName})` : ""}`;
|
||||
const option = { ...app.locationOption, label, icon: app.logo, slug: app.slug };
|
||||
const option = {
|
||||
...app.locationOption,
|
||||
label,
|
||||
icon: app.logo,
|
||||
slug: app.slug,
|
||||
...(app.credential ? { credentialId: app.credential.id, teamName: app.credential.team?.name } : {}),
|
||||
};
|
||||
if (apps[groupByCategory]) {
|
||||
apps[groupByCategory] = [...apps[groupByCategory], option];
|
||||
} else {
|
||||
|
|
|
@ -236,7 +236,7 @@ export class Cal {
|
|||
}: {
|
||||
calLink: string;
|
||||
queryObject?: PrefillAndIframeAttrsConfig & { guest?: string | string[] };
|
||||
calOrigin?: string;
|
||||
calOrigin: string | null;
|
||||
}) {
|
||||
const iframe = (this.iframe = document.createElement("iframe"));
|
||||
iframe.className = "cal-embed";
|
||||
|
@ -473,10 +473,12 @@ class CalApi {
|
|||
}
|
||||
|
||||
config.embedType = "inline";
|
||||
const calConfig = this.cal.getConfig();
|
||||
|
||||
const iframe = this.cal.createIframe({
|
||||
calLink,
|
||||
queryObject: withColorScheme(Cal.getQueryObject(config), containerEl),
|
||||
calOrigin: calConfig.calOrigin,
|
||||
});
|
||||
|
||||
iframe.style.height = "100%";
|
||||
|
@ -555,6 +557,7 @@ class CalApi {
|
|||
modal({
|
||||
calLink,
|
||||
config = {},
|
||||
calOrigin,
|
||||
__prerender = false,
|
||||
}: {
|
||||
calLink: string;
|
||||
|
@ -607,6 +610,7 @@ class CalApi {
|
|||
iframe = this.cal.createIframe({
|
||||
calLink,
|
||||
queryObject,
|
||||
calOrigin: calOrigin || null,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ import TeamPill, { TeamRole } from "./TeamPill";
|
|||
interface Props {
|
||||
team: RouterOutputs["viewer"]["teams"]["get"];
|
||||
member: RouterOutputs["viewer"]["teams"]["get"]["members"][number];
|
||||
isOrgAdminOrOwner: boolean | undefined;
|
||||
}
|
||||
|
||||
/** TODO: Migrate the one in apps/web to tRPC package */
|
||||
|
@ -109,7 +110,8 @@ export default function MemberListItem(props: Props) {
|
|||
(props.member.role !== MembershipRole.OWNER ||
|
||||
ownersInTeam() > 1 ||
|
||||
props.member.id !== currentUserId)) ||
|
||||
(props.team.membership?.role === MembershipRole.ADMIN && props.member.role !== MembershipRole.OWNER);
|
||||
(props.team.membership?.role === MembershipRole.ADMIN && props.member.role !== MembershipRole.OWNER) ||
|
||||
props.isOrgAdminOrOwner;
|
||||
const impersonationMode =
|
||||
editMode &&
|
||||
!props.member.disableImpersonation &&
|
||||
|
|
|
@ -22,13 +22,14 @@ type Team = RouterOutputs["viewer"]["teams"]["get"];
|
|||
|
||||
interface MembersListProps {
|
||||
team: Team | undefined;
|
||||
isOrgAdminOrOwner: boolean | undefined;
|
||||
}
|
||||
|
||||
const checkIfExist = (comp: string, query: string) =>
|
||||
comp.toLowerCase().replace(/\s+/g, "").includes(query.toLowerCase().replace(/\s+/g, ""));
|
||||
|
||||
function MembersList(props: MembersListProps) {
|
||||
const { team } = props;
|
||||
const { team, isOrgAdminOrOwner } = props;
|
||||
const { t } = useLocale();
|
||||
const [query, setQuery] = useState<string>("");
|
||||
|
||||
|
@ -56,7 +57,14 @@ function MembersList(props: MembersListProps) {
|
|||
{membersList?.length && team ? (
|
||||
<ul className="divide-subtle border-subtle divide-y rounded-md border ">
|
||||
{membersList.map((member) => {
|
||||
return <MemberListItem key={member.id} team={team} member={member} />;
|
||||
return (
|
||||
<MemberListItem
|
||||
key={member.id}
|
||||
team={team}
|
||||
member={member}
|
||||
isOrgAdminOrOwner={isOrgAdminOrOwner}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
) : null}
|
||||
|
@ -161,7 +169,7 @@ const MembersView = () => {
|
|||
|
||||
{((team?.isPrivate && isAdmin) || !team?.isPrivate || isOrgAdminOrOwner) && (
|
||||
<>
|
||||
<MembersList team={team} />
|
||||
<MembersList team={team} isOrgAdminOrOwner={isOrgAdminOrOwner} />
|
||||
<hr className="border-subtle my-8" />
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -13,7 +13,9 @@ export function defaultResponder<T>(f: Handle<T>) {
|
|||
performance.mark("Start");
|
||||
const result = await f(req, res);
|
||||
ok = true;
|
||||
if (result) res.json(result);
|
||||
if (result && !res.writableEnded) {
|
||||
res.json(result);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
const error = getServerErrorFromUnknown(err);
|
||||
|
|
|
@ -23,7 +23,8 @@ export const removeMemberHandler = async ({ ctx, input }: RemoveMemberOptions) =
|
|||
const isOrgAdmin = ctx.user.organizationId
|
||||
? await isTeamAdmin(ctx.user.id, ctx.user.organizationId)
|
||||
: false;
|
||||
if (!isAdmin && ctx.user.id !== input.memberId) throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||
if (!(isAdmin || isOrgAdmin) && ctx.user.id !== input.memberId)
|
||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||
// Only a team owner can remove another team owner.
|
||||
if ((await isTeamOwner(input.memberId, input.teamId)) && !(await isTeamOwner(ctx.user.id, input.teamId)))
|
||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||
|
|
Loading…
Reference in New Issue
Block a user