Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into feat/lock-event-types…
Browse files Browse the repository at this point in the history
…-orgs
  • Loading branch information
sean-brydon committed Mar 12, 2024
2 parents cd8a55a + 54db9a4 commit b709b08
Show file tree
Hide file tree
Showing 108 changed files with 2,285 additions and 661 deletions.
Expand Up @@ -3,13 +3,17 @@ import type { z } from "zod";

import { HttpError } from "@calcom/lib/http-error";

import type { schemaEventTypeBaseBodyParams } from "~/lib/validations/event-type";
import type { schemaEventTypeCreateBodyParams } from "~/lib/validations/event-type";

export default async function checkTeamEventEditPermission(
req: NextApiRequest,
body: Pick<z.infer<typeof schemaEventTypeBaseBodyParams>, "teamId">
body: Pick<z.infer<typeof schemaEventTypeCreateBodyParams>, "teamId" | "userId">
) {
const { prisma, userId } = req;
const { prisma, isAdmin } = req;
let userId = req.userId;
if (isAdmin && body.userId) {
userId = body.userId;
}
if (body.teamId) {
const membership = await prisma.membership.findFirst({
where: {
Expand Down
15 changes: 13 additions & 2 deletions apps/web/components/apps/AppList.tsx
Expand Up @@ -5,7 +5,7 @@ import { InstallAppButton } from "@calcom/app-store/components";
import { getEventLocationTypeFromApp, type EventLocationType } from "@calcom/app-store/locations";
import type { CredentialOwner } from "@calcom/app-store/types";
import { AppSetDefaultLinkDialog } from "@calcom/features/apps/components/AppSetDefaultLinkDialog";
import { BulkEditDefaultConferencingModal } from "@calcom/features/eventtypes/components/BulkEditDefaultConferencingModal";
import { BulkEditDefaultForEventsModal } from "@calcom/features/eventtypes/components/BulkEditDefaultForEventsModal";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { AppCategories } from "@calcom/prisma/enums";
import { trpc, type RouterOutputs } from "@calcom/trpc";
Expand Down Expand Up @@ -154,6 +154,12 @@ export const AppList = ({ data, handleDisconnect, variant, listClassName }: AppL
});

const { t } = useLocale();
const updateLocationsMutation = trpc.viewer.eventTypes.bulkUpdateToDefaultLocation.useMutation({
onSuccess: () => {
utils.viewer.getUsersDefaultConferencingApp.invalidate();
setBulkUpdateModal(false);
},
});
return (
<>
<List className={listClassName}>
Expand All @@ -173,7 +179,12 @@ export const AppList = ({ data, handleDisconnect, variant, listClassName }: AppL
)}

{bulkUpdateModal && (
<BulkEditDefaultConferencingModal open={bulkUpdateModal} setOpen={setBulkUpdateModal} />
<BulkEditDefaultForEventsModal
bulkUpdateFunction={updateLocationsMutation.mutate}
open={bulkUpdateModal}
setOpen={setBulkUpdateModal}
isPending={updateLocationsMutation.isPending}
/>
)}
</>
);
Expand Down
10 changes: 6 additions & 4 deletions apps/web/components/booking/BookingListItem.tsx
Expand Up @@ -162,7 +162,7 @@ function BookingListItem(booking: BookingItemProps) {
let bookedActions: ActionType[] = [
{
id: "cancel",
label: isTabRecurring && isRecurring ? t("cancel_all_remaining") : t("cancel"),
label: isTabRecurring && isRecurring ? t("cancel_all_remaining") : t("cancel_event"),
/* When cancelling we need to let the UI and the API know if the intention is to
cancel all remaining bookings or just that booking instance. */
href: `/booking/${booking.uid}?cancel=true${
Expand Down Expand Up @@ -297,6 +297,8 @@ function BookingListItem(booking: BookingItemProps) {
},
];

const showPendingPayment = paymentAppData.enabled && booking.payment.length && !booking.paid;

return (
<>
<RescheduleDialog
Expand Down Expand Up @@ -463,7 +465,7 @@ function BookingListItem(booking: BookingItemProps) {
{booking.eventType.team.name}
</Badge>
)}
{!!booking?.eventType?.price && !booking.paid && (
{showPendingPayment && (
<Badge className="ltr:mr-2 rtl:ml-2 sm:hidden" variant="orange">
{t("pending_payment")}
</Badge>
Expand All @@ -490,8 +492,8 @@ function BookingListItem(booking: BookingItemProps) {
{title}
<span> </span>

{paymentAppData.enabled && !booking.paid && booking.payment.length && (
<Badge className="me-2 ms-2 hidden sm:inline-flex" variant="orange">
{showPendingPayment && (
<Badge className="hidden sm:inline-flex" variant="orange">
{t("pending_payment")}
</Badge>
)}
Expand Down
116 changes: 58 additions & 58 deletions apps/web/components/eventtype/EventAdvancedTab.tsx
@@ -1,7 +1,7 @@
import { InfoIcon } from "lucide-react";
import dynamic from "next/dynamic";
import Link from "next/link";
import type { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
import type { EventTypeSetupProps } from "pages/event-types/[type]";
import { useEffect, useState } from "react";
import { Controller, useFormContext } from "react-hook-form";
import type { z } from "zod";
Expand All @@ -15,6 +15,7 @@ import {
allowDisablingAttendeeConfirmationEmails,
allowDisablingHostConfirmationEmails,
} from "@calcom/features/ee/workflows/lib/allowDisablingStandardEmails";
import type { FormValues } from "@calcom/features/eventtypes/lib/types";
import { FormBuilder } from "@calcom/features/form-builder/FormBuilder";
import type { EditableSchema } from "@calcom/features/form-builder/schema";
import { BookerLayoutSelector } from "@calcom/features/settings/BookerLayoutSelector";
Expand Down Expand Up @@ -55,7 +56,6 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
formMethods.getValues("useEventTypeDestinationCalendarEmail")
);
const [hashedUrl, setHashedUrl] = useState(eventType.hashedLink?.link);

const bookingFields: Prisma.JsonObject = {};

const workflows = eventType.workflows.map((workflowOnEventType) => workflowOnEventType.workflow);
Expand Down Expand Up @@ -86,6 +86,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,

useEffect(() => {
!hashedUrl && setHashedUrl(generateHashedLink(formMethods.getValues("users")[0]?.id ?? team?.id));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [formMethods.getValues("users"), hashedUrl, team?.id]);

const toggleGuests = (enabled: boolean) => {
Expand All @@ -108,22 +109,26 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
);
};

const { shouldLockDisableProps } = useLockedFieldsManager(
formMethods.getValues(),
t("locked_fields_admin_description"),
t("locked_fields_member_description")
);
const { isChildrenManagedEventType, isManagedEventType, shouldLockDisableProps } = useLockedFieldsManager({
eventType,
translate: t,
formMethods,
});
const eventNamePlaceholder = getEventName({
...eventNameObject,
eventName: formMethods.watch("eventName"),
});

const successRedirectUrlLocked = shouldLockDisableProps("successRedirectUrl");
const seatsLocked = shouldLockDisableProps("seatsPerTimeSlotEnabled");
const requiresBookerEmailVerificationProps = shouldLockDisableProps("requiresBookerEmailVerification");
const hideCalendarNotesLocked = shouldLockDisableProps("hideCalendarNotes");
const lockTimeZoneToggleOnBookingPageLocked = shouldLockDisableProps("lockTimeZoneToggleOnBookingPage");

const closeEventNameTip = () => setShowEventNameTip(false);
const displayDestinationCalendarSelector =
!!connectedCalendarsQuery.data?.connectedCalendars.length && !team;
!!connectedCalendarsQuery.data?.connectedCalendars.length && (!team || isChildrenManagedEventType);

const verifiedSecondaryEmails = [
{
label: user?.email || "",
Expand Down Expand Up @@ -181,6 +186,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
<Button
color="minimal"
size="sm"
{...(shouldLockDisableProps("eventName").disabled ? { disabled: true } : {})}
aria-label="edit custom name"
className="hover:stroke-3 hover:text-emphasis min-w-fit !py-0 px-0 hover:bg-transparent"
onClick={() => setShowEventNameTip((old) => !old)}>
Expand Down Expand Up @@ -233,9 +239,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
</div>
)}
</div>

<BookerLayoutSelector fallbackToUserSettings isDark={selectedThemeIsDark} isOuterBorder={true} />

<div className="border-subtle space-y-6 rounded-lg border p-6">
<FormBuilder
title={t("booking_questions_title")}
Expand All @@ -250,15 +254,13 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
}}
/>
</div>

<RequiresConfirmationController
eventType={eventType}
seatsEnabled={seatsEnabled}
metadata={formMethods.getValues("metadata")}
requiresConfirmation={requiresConfirmation}
onRequiresConfirmation={setRequiresConfirmation}
/>

<Controller
name="requiresBookerEmailVerification"
render={({ field: { value, onChange } }) => (
Expand All @@ -268,31 +270,29 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
switchContainerClassName="border-subtle rounded-lg border py-6 px-4 sm:px-6"
title={t("requires_booker_email_verification")}
data-testid="requires-booker-email-verification"
{...shouldLockDisableProps("requiresBookerEmailVerification")}
{...requiresBookerEmailVerificationProps}
description={t("description_requires_booker_email_verification")}
checked={value}
onCheckedChange={(e) => onChange(e)}
/>
)}
/>

<Controller
name="hideCalendarNotes"
render={({ field: { value, onChange } }) => (
<SettingsToggle
labelClassName="text-sm"
toggleSwitchAtTheEnd={true}
switchContainerClassName="border-subtle rounded-lg border py-6 px-4 sm:px-6"
title={t("disable_notes")}
data-testid="disable-notes"
{...shouldLockDisableProps("hideCalendarNotes")}
title={t("disable_notes")}
{...hideCalendarNotesLocked}
description={t("disable_notes_description")}
checked={value}
onCheckedChange={(e) => onChange(e)}
/>
)}
/>

<Controller
name="successRedirectUrl"
render={({ field: { value, onChange } }) => (
Expand Down Expand Up @@ -339,7 +339,6 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
</>
)}
/>

<SettingsToggle
labelClassName="text-sm"
toggleSwitchAtTheEnd={true}
Expand All @@ -359,52 +358,53 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
<Info className="ml-1.5 h-4 w-4 cursor-pointer" />
</a>
}
{...shouldLockDisableProps("hashedLinkCheck")}
{...shouldLockDisableProps("hashedLink")}
description={t("private_link_description", { appName: APP_NAME })}
checked={hashedLinkVisible}
onCheckedChange={(e) => {
formMethods.setValue("hashedLink", e ? hashedUrl : undefined, { shouldDirty: true });
setHashedLinkVisible(e);
}}>
<div className="border-subtle rounded-b-lg border border-t-0 p-6">
{!IS_VISUAL_REGRESSION_TESTING && (
<TextField
disabled
name="hashedLink"
label={t("private_link_label")}
data-testid="generated-hash-url"
labelSrOnly
type="text"
hint={t("private_link_hint")}
defaultValue={placeholderHashedLink}
addOnSuffix={
<Tooltip
content={
formMethods.getValues("hashedLink") ? t("copy_to_clipboard") : t("enabled_after_update")
}>
<Button
color="minimal"
size="sm"
type="button"
className="hover:stroke-3 hover:text-emphasis min-w-fit !py-0 px-0 hover:bg-transparent"
aria-label="copy link"
onClick={() => {
navigator.clipboard.writeText(placeholderHashedLink);
if (formMethods.getValues("hashedLink")) {
showToast(t("private_link_copied"), "success");
} else {
showToast(t("enabled_after_update_description"), "warning");
}
}}>
<Copy className="h-4 w-4" />
</Button>
</Tooltip>
}
/>
)}
</div>
{!isManagedEventType && (
<div className="border-subtle rounded-b-lg border border-t-0 p-6">
{!IS_VISUAL_REGRESSION_TESTING && (
<TextField
disabled
name="hashedLink"
label={t("private_link_label")}
data-testid="generated-hash-url"
labelSrOnly
type="text"
hint={t("private_link_hint")}
defaultValue={placeholderHashedLink}
addOnSuffix={
<Tooltip
content={
formMethods.getValues("hashedLink") ? t("copy_to_clipboard") : t("enabled_after_update")
}>
<Button
color="minimal"
size="sm"
type="button"
className="hover:stroke-3 hover:text-emphasis min-w-fit !py-0 px-0 hover:bg-transparent"
aria-label="copy link"
onClick={() => {
navigator.clipboard.writeText(placeholderHashedLink);
if (formMethods.getValues("hashedLink")) {
showToast(t("private_link_copied"), "success");
} else {
showToast(t("enabled_after_update_description"), "warning");
}
}}>
<Copy className="h-4 w-4" />
</Button>
</Tooltip>
}
/>
)}
</div>
)}
</SettingsToggle>

<Controller
name="seatsPerTimeSlotEnabled"
render={({ field: { value, onChange } }) => (
Expand Down Expand Up @@ -504,7 +504,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
toggleSwitchAtTheEnd={true}
switchContainerClassName="border-subtle rounded-lg border py-6 px-4 sm:px-6"
title={t("lock_timezone_toggle_on_booking_page")}
{...shouldLockDisableProps("lockTimeZoneToggleOnBookingPage")}
{...lockTimeZoneToggleOnBookingPageLocked}
description={t("description_lock_timezone_toggle_on_booking_page")}
checked={value}
onCheckedChange={(e) => onChange(e)}
Expand Down

0 comments on commit b709b08

Please sign in to comment.