Merge branch 'main' into fix/engines-16
This commit is contained in:
commit
edfffd6449
|
@ -127,3 +127,6 @@ EMAIL_SERVER_PASSWORD='<office365_password>'
|
|||
## @see https://support.google.com/accounts/answer/185833
|
||||
# EMAIL_SERVER_PASSWORD='<gmail_app_password>'
|
||||
# **********************************************************************************************************
|
||||
|
||||
# Set the following value to true if you wish to enable Team Impersonation
|
||||
NEXT_PUBLIC_TEAM_IMPERSONATION=false
|
||||
|
|
|
@ -10,5 +10,8 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v4
|
||||
# Checking the actor will prevent your Action run failing on non-Dependabot
|
||||
# PRs but also ensures that it only does work for Dependabot PRs.
|
||||
if: ${{ github.actor == 'github-actions[bot]' }}
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 8a796ab11dd6663be5e582e7bac49f0aabd92dc3
|
||||
Subproject commit a26db083faaa79a40f96dddac888ba2c2bea921e
|
|
@ -4,11 +4,11 @@ import { OptionProps } from "react-select";
|
|||
|
||||
import { InstallAppButton } from "@calcom/app-store/components";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import type { App } from "@calcom/types/App";
|
||||
import { Button } from "@calcom/ui";
|
||||
|
||||
import { QueryCell } from "@lib/QueryCell";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
interface AdditionalCalendarSelectorProps {
|
||||
isLoading?: boolean;
|
||||
|
|
|
@ -16,12 +16,11 @@ import useAddAppMutation from "@calcom/app-store/_utils/useAddAppMutation";
|
|||
import { InstallAppButton } from "@calcom/app-store/components";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { App as AppType } from "@calcom/types/App";
|
||||
import { Button, SkeletonButton } from "@calcom/ui";
|
||||
import LicenseRequired from "@ee/components/LicenseRequired";
|
||||
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import Shell from "@components/Shell";
|
||||
import Badge from "@components/ui/Badge";
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@ import React, { useEffect, useState } from "react";
|
|||
import Select from "react-select";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
|
||||
import { trpc } from "@lib/trpc";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
|
||||
interface Props {
|
||||
onChange: (value: { externalId: string; integration: string }) => void;
|
||||
|
|
|
@ -8,12 +8,12 @@ import { components, ControlProps } from "react-select";
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { EventType } from "@calcom/prisma/client";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Button, Switch } from "@calcom/ui";
|
||||
import { Dialog, DialogClose, DialogContent } from "@calcom/ui/Dialog";
|
||||
import { InputLeading, Label, TextArea, TextField } from "@calcom/ui/form/fields";
|
||||
|
||||
import { EMBED_LIB_URL, WEBAPP_URL } from "@lib/config/constants";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import NavTabs from "@components/NavTabs";
|
||||
import ColorPicker from "@components/ui/colorpicker";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useTranslation } from "next-i18next";
|
||||
import { useEffect } from "react";
|
||||
|
||||
import { trpc } from "@lib/trpc";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
|
||||
export function useViewerI18n() {
|
||||
return trpc.useQuery(["viewer.public.i18n"], {
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
// This component is abstracted from /event-types/[type] for common usecase.
|
||||
import { PencilIcon } from "@heroicons/react/solid";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function PencilEdit({
|
||||
value,
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
onChange = () => {},
|
||||
placeholder = "",
|
||||
readOnly = false,
|
||||
}: {
|
||||
value: string;
|
||||
onChange?: (value: string) => void;
|
||||
placeholder?: string;
|
||||
readOnly?: boolean;
|
||||
}) {
|
||||
const [editIcon, setEditIcon] = useState(true);
|
||||
const onDivClick = !readOnly
|
||||
? () => {
|
||||
return setEditIcon(false);
|
||||
}
|
||||
: // eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
() => {};
|
||||
return (
|
||||
<div className="group relative min-h-[28px] cursor-pointer" onClick={onDivClick}>
|
||||
{editIcon ? (
|
||||
<>
|
||||
<h1
|
||||
style={{ fontSize: 22, letterSpacing: "-0.0009em" }}
|
||||
className="inline-block pl-0 text-gray-900 focus:text-black group-hover:text-gray-500">
|
||||
{value}
|
||||
</h1>
|
||||
{!readOnly ? (
|
||||
<PencilIcon className="ml-1 -mt-1 inline h-4 w-4 text-gray-700 group-hover:text-gray-500" />
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<div style={{ marginBottom: -11 }}>
|
||||
<input
|
||||
type="text"
|
||||
autoFocus
|
||||
style={{ top: -6, fontSize: 22 }}
|
||||
required
|
||||
className="relative h-10 w-full cursor-pointer border-none bg-transparent pl-0 text-gray-900 hover:text-gray-700 focus:text-black focus:outline-none focus:ring-0"
|
||||
placeholder={placeholder}
|
||||
defaultValue={value}
|
||||
onBlur={(e) => {
|
||||
setEditIcon(true);
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -24,6 +24,7 @@ import { Toaster } from "react-hot-toast";
|
|||
import { useIsEmbed } from "@calcom/embed-core/embed-iframe";
|
||||
import { WEBAPP_URL, JOIN_SLACK, ROADMAP } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import Dropdown, {
|
||||
DropdownMenuContent,
|
||||
|
@ -39,7 +40,6 @@ import ErrorBoundary from "@lib/ErrorBoundary";
|
|||
import classNames from "@lib/classNames";
|
||||
import { shouldShowOnboarding } from "@lib/getting-started";
|
||||
import useMeQuery from "@lib/hooks/useMeQuery";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import CustomBranding from "@components/CustomBranding";
|
||||
import { KBarRoot, KBarContent, KBarTrigger } from "@components/Kbar";
|
||||
|
@ -494,7 +494,17 @@ function UserDropdown({ small }: { small?: boolean }) {
|
|||
const { t } = useLocale();
|
||||
const query = useMeQuery();
|
||||
const user = query.data;
|
||||
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
const Beacon = window.Beacon;
|
||||
// window.Beacon is defined when user actually opens up HelpScout and username is available here. On every re-render update session info, so that it is always latest.
|
||||
Beacon &&
|
||||
Beacon("session-data", {
|
||||
username: user?.username || "Unknown",
|
||||
screenResolution: `${screen.width}x${screen.height}`,
|
||||
});
|
||||
});
|
||||
const mutation = trpc.useMutation("viewer.away", {
|
||||
onSettled() {
|
||||
utils.invalidateQueries("viewer.me");
|
||||
|
@ -570,7 +580,7 @@ function UserDropdown({ small }: { small?: boolean }) {
|
|||
<DropdownMenuItem>
|
||||
<a
|
||||
onClick={() => {
|
||||
mutation.mutate({ away: user?.away });
|
||||
mutation.mutate({ away: !user?.away });
|
||||
utils.invalidateQueries("viewer.me");
|
||||
}}
|
||||
className="flex min-w-max cursor-pointer px-4 py-2 text-sm hover:bg-gray-100 hover:text-gray-900">
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import Link from "next/link";
|
||||
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import Badge from "@components/ui/Badge";
|
||||
|
||||
interface AppCardProps {
|
||||
|
|
|
@ -2,11 +2,11 @@ import { signIn } from "next-auth/react";
|
|||
import { Dispatch, SetStateAction } from "react";
|
||||
import { useFormContext } from "react-hook-form";
|
||||
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
interface Props {
|
||||
email: string;
|
||||
|
|
|
@ -4,12 +4,12 @@ import { useForm } from "react-hook-form";
|
|||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Button } from "@calcom/ui";
|
||||
import { Dialog, DialogClose, DialogContent, DialogTrigger } from "@calcom/ui/Dialog";
|
||||
import { Form } from "@calcom/ui/form/fields";
|
||||
|
||||
import { HttpError } from "@lib/core/http/error";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
export function NewScheduleButton({ name = "new-schedule" }: { name?: string }) {
|
||||
const router = useRouter();
|
||||
|
|
|
@ -8,7 +8,8 @@ import { GroupBase, Props } from "react-select";
|
|||
import dayjs, { Dayjs, ConfigType } from "@calcom/dayjs";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import Dropdown, { DropdownMenuTrigger, DropdownMenuContent } from "@calcom/ui/Dropdown";
|
||||
import Dropdown, { DropdownMenuContent } from "@calcom/ui/Dropdown";
|
||||
import { Tooltip } from "@calcom/ui/Tooltip";
|
||||
|
||||
import { defaultDayRange } from "@lib/availability";
|
||||
import { weekdayNames } from "@lib/core/i18n/weekday";
|
||||
|
@ -202,7 +203,7 @@ export const DayRanges = ({
|
|||
const { setValue, watch } = useFormContext();
|
||||
// XXX: Hack to make copying times work; `fields` is out of date until save.
|
||||
const watcher = watch(name);
|
||||
|
||||
const { t } = useLocale();
|
||||
const { fields, replace, append, remove } = useFieldArray({
|
||||
name,
|
||||
});
|
||||
|
@ -242,16 +243,18 @@ export const DayRanges = ({
|
|||
</div>
|
||||
{index === 0 && (
|
||||
<div className="absolute top-2 right-0 text-right sm:relative sm:top-0 sm:flex-grow">
|
||||
<Button
|
||||
className="text-neutral-400"
|
||||
type="button"
|
||||
color="minimal"
|
||||
size="icon"
|
||||
StartIcon={PlusIcon}
|
||||
onClick={handleAppend}
|
||||
/>
|
||||
<Tooltip content={t("add_time_availability") as string}>
|
||||
<Button
|
||||
className="text-neutral-400"
|
||||
type="button"
|
||||
color="minimal"
|
||||
size="icon"
|
||||
StartIcon={PlusIcon}
|
||||
onClick={handleAppend}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Dropdown>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Tooltip content={t("duplicate") as string}>
|
||||
<Button
|
||||
type="button"
|
||||
color="minimal"
|
||||
|
@ -259,7 +262,7 @@ export const DayRanges = ({
|
|||
StartIcon={DuplicateIcon}
|
||||
onClick={handleAppend}
|
||||
/>
|
||||
</DropdownMenuTrigger>
|
||||
</Tooltip>
|
||||
<DropdownMenuContent>
|
||||
<CopyTimes
|
||||
disabled={[parseInt(name.substring(name.lastIndexOf(".") + 1), 10)]}
|
||||
|
|
|
@ -5,11 +5,10 @@ import { Fragment } from "react";
|
|||
import { availabilityAsString } from "@calcom/lib/availability";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Availability } from "@calcom/prisma/client";
|
||||
import { inferQueryOutput } from "@calcom/trpc/react";
|
||||
import { Button } from "@calcom/ui";
|
||||
import Dropdown, { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@calcom/ui/Dropdown";
|
||||
|
||||
import { inferQueryOutput } from "@lib/trpc";
|
||||
|
||||
export function ScheduleListItem({
|
||||
schedule,
|
||||
deleteFunction,
|
||||
|
|
|
@ -6,13 +6,12 @@ import { FC, useEffect, useState } from "react";
|
|||
import dayjs, { Dayjs } from "@calcom/dayjs";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { nameOfDay } from "@calcom/lib/weekday";
|
||||
import type { Slot } from "@calcom/trpc/server/routers/viewer/slots";
|
||||
import { SkeletonContainer, SkeletonText } from "@calcom/ui";
|
||||
|
||||
import classNames from "@lib/classNames";
|
||||
import { timeZone } from "@lib/clock";
|
||||
|
||||
import type { Slot } from "@server/routers/viewer/slots";
|
||||
|
||||
type AvailableTimesProps = {
|
||||
timeFormat: string;
|
||||
eventTypeId: number;
|
||||
|
|
|
@ -18,6 +18,7 @@ import classNames from "@calcom/lib/classNames";
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
|
||||
import { inferQueryInput, inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/Dialog";
|
||||
import { Tooltip } from "@calcom/ui/Tooltip";
|
||||
|
@ -28,7 +29,6 @@ import useMeQuery from "@lib/hooks/useMeQuery";
|
|||
import { linkValueToString } from "@lib/linkValueToString";
|
||||
import { LocationType } from "@lib/location";
|
||||
import { extractRecurringDates } from "@lib/parseDate";
|
||||
import { inferQueryInput, inferQueryOutput, trpc } from "@lib/trpc";
|
||||
|
||||
import { EditLocationDialog } from "@components/dialog/EditLocationDialog";
|
||||
import { RescheduleDialog } from "@components/dialog/RescheduleDialog";
|
||||
|
|
|
@ -35,6 +35,7 @@ import { CAL_URL, WEBSITE_URL } from "@calcom/lib/constants";
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { getRecurringFreq } from "@calcom/lib/recurringStrings";
|
||||
import { localStorage } from "@calcom/lib/webstorage";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import DatePicker from "@calcom/ui/booker/DatePicker";
|
||||
|
||||
import { timeZone as localStorageTimeZone } from "@lib/clock";
|
||||
|
@ -44,7 +45,6 @@ import useTheme from "@lib/hooks/useTheme";
|
|||
import { isBrandingHidden } from "@lib/isBrandingHidden";
|
||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry";
|
||||
import { detectBrowserTimeFormat } from "@lib/timeFormat";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import CustomBranding from "@components/CustomBranding";
|
||||
import AvailableTimes from "@components/booking/AvailableTimes";
|
||||
|
|
|
@ -8,6 +8,7 @@ import { z } from "zod";
|
|||
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import { Button } from "@calcom/ui";
|
||||
import { Dialog, DialogContent } from "@calcom/ui/Dialog";
|
||||
import { Form } from "@calcom/ui/form/fields";
|
||||
|
@ -16,7 +17,6 @@ import { QueryCell } from "@lib/QueryCell";
|
|||
import { linkValueToString } from "@lib/linkValueToString";
|
||||
import { LocationType } from "@lib/location";
|
||||
import { LocationOptionsToString } from "@lib/locationOptions";
|
||||
import { inferQueryOutput, trpc } from "@lib/trpc";
|
||||
|
||||
import CheckboxField from "@components/ui/form/CheckboxField";
|
||||
import type PhoneInputType from "@components/ui/form/PhoneInput";
|
||||
|
|
|
@ -5,12 +5,12 @@ import { useMutation } from "react-query";
|
|||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/Dialog";
|
||||
import { TextArea } from "@calcom/ui/form/fields";
|
||||
|
||||
import * as fetchWrapper from "@lib/core/http/fetch-wrapper";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
interface IRescheduleDialog {
|
||||
isOpenDialog: boolean;
|
||||
|
|
|
@ -6,9 +6,11 @@ import { useEffect } from "react";
|
|||
import { useForm } from "react-hook-form";
|
||||
import type { z } from "zod";
|
||||
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { createEventTypeInput } from "@calcom/prisma/zod/custom/eventtype";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import { Button } from "@calcom/ui/Button";
|
||||
import { Dialog, DialogClose, DialogContent } from "@calcom/ui/Dialog";
|
||||
|
@ -23,7 +25,6 @@ import { Form, InputLeading, TextAreaField, TextField } from "@calcom/ui/form/fi
|
|||
|
||||
import { HttpError } from "@lib/core/http/error";
|
||||
import { slugify } from "@lib/slugify";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import Avatar from "@components/ui/Avatar";
|
||||
import * as RadioArea from "@components/ui/form/radio-area";
|
||||
|
@ -164,7 +165,7 @@ export default function CreateEventTypeButton(props: Props) {
|
|||
onSelect={() => openModal(option)}>
|
||||
<Avatar
|
||||
alt={option.name || ""}
|
||||
imageSrc={option.image}
|
||||
imageSrc={option.image || `${WEBAPP_URL}/${option.slug}/avatar.png`} // if no image, use default avatar
|
||||
size={6}
|
||||
className="inline ltr:mr-2 rtl:ml-2"
|
||||
/>
|
||||
|
|
|
@ -4,12 +4,12 @@ import { useMutation } from "react-query";
|
|||
import { InstallAppButton } from "@calcom/app-store/components";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import Switch from "@calcom/ui/Switch";
|
||||
|
||||
import { QueryCell } from "@lib/QueryCell";
|
||||
import { inferQueryOutput, trpc } from "@lib/trpc";
|
||||
|
||||
import AdditionalCalendarSelector from "@components/AdditionalCalendarSelector";
|
||||
import DestinationCalendarSelector from "@components/DestinationCalendarSelector";
|
||||
|
|
|
@ -3,11 +3,10 @@ import { useMutation } from "react-query";
|
|||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { ButtonBaseProps } from "@calcom/ui/Button";
|
||||
import { Dialog } from "@calcom/ui/Dialog";
|
||||
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent";
|
||||
|
||||
export default function DisconnectIntegration(props: {
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import Badge from "@components/ui/Badge";
|
||||
|
||||
const DisableUserImpersonation = ({ disableImpersonation }: { disableImpersonation: boolean }) => {
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
|
||||
import Badge from "@components/ui/Badge";
|
||||
|
||||
const DisableTeamImpersonation = ({ teamId, memberId }: { teamId: number; memberId: number }) => {
|
||||
const { t } = useLocale();
|
||||
|
||||
const utils = trpc.useContext();
|
||||
|
||||
const query = trpc.useQuery(["viewer.teams.getMembershipbyUser", { teamId, memberId }]);
|
||||
|
||||
const mutation = trpc.useMutation("viewer.teams.updateMembership", {
|
||||
onSuccess: async () => {
|
||||
showToast(t("your_user_profile_updated_successfully"), "success");
|
||||
await utils.invalidateQueries(["viewer.teams.getMembershipbyUser"]);
|
||||
},
|
||||
async onSettled() {
|
||||
await utils.invalidateQueries(["viewer.public.i18n"]);
|
||||
},
|
||||
});
|
||||
if (query.isLoading) return <></>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3 className="font-cal mt-7 pb-4 text-xl leading-6 text-gray-900">{t("settings")}</h3>
|
||||
<div className="-mx-0 rounded-sm border border-neutral-200 bg-white px-4 pb-4 sm:px-6">
|
||||
<div className="flex flex-col justify-between pt-4 sm:flex-row">
|
||||
<div>
|
||||
<div className="flex flex-row items-center">
|
||||
<h2 className="font-cal font-bold leading-6 text-gray-900">
|
||||
{t("user_impersonation_heading")}
|
||||
</h2>
|
||||
<Badge
|
||||
className="ml-2 text-xs"
|
||||
variant={!query.data?.disableImpersonation ? "success" : "gray"}>
|
||||
{!query.data?.disableImpersonation ? t("enabled") : t("disabled")}
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="text-sm text-gray-700">{t("team_impersonation_description")}</p>
|
||||
</div>
|
||||
<div className="mt-5 sm:mt-0 sm:self-center">
|
||||
<Button
|
||||
type="submit"
|
||||
color="secondary"
|
||||
onClick={() =>
|
||||
!query.data?.disableImpersonation
|
||||
? mutation.mutate({ teamId, memberId, disableImpersonation: true })
|
||||
: mutation.mutate({ teamId, memberId, disableImpersonation: false })
|
||||
}>
|
||||
{!query.data?.disableImpersonation ? t("disable") : t("enable")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DisableTeamImpersonation;
|
|
@ -2,10 +2,9 @@ import { MembershipRole } from "@prisma/client";
|
|||
import { SyntheticEvent, useMemo, useState } from "react";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import ModalContainer from "@components/ui/ModalContainer";
|
||||
import Select from "@components/ui/form/Select";
|
||||
|
||||
|
|
|
@ -3,13 +3,13 @@ import { InformationCircleIcon } from "@heroicons/react/solid";
|
|||
import { MembershipRole } from "@prisma/client";
|
||||
import React, { useState, SyntheticEvent, useMemo } from "react";
|
||||
|
||||
import { TeamWithMembers } from "@calcom/lib/server/queries/teams";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogContent, DialogFooter } from "@calcom/ui/Dialog";
|
||||
import { TextField } from "@calcom/ui/form/fields";
|
||||
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import { TeamWithMembers } from "@lib/queries/teams";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import Select from "@components/ui/form/Select";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { inferQueryOutput } from "@lib/trpc";
|
||||
import { inferQueryOutput } from "@calcom/trpc/react";
|
||||
|
||||
import MemberListItem from "./MemberListItem";
|
||||
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { PencilIcon, UserRemoveIcon } from "@heroicons/react/outline";
|
||||
import { LockClosedIcon, PencilIcon, UserRemoveIcon } from "@heroicons/react/outline";
|
||||
import { ClockIcon, DotsHorizontalIcon, ExternalLinkIcon } from "@heroicons/react/solid";
|
||||
import { MembershipRole } from "@prisma/client";
|
||||
import { signIn } from "next-auth/react";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogTrigger } from "@calcom/ui/Dialog";
|
||||
import Dropdown, {
|
||||
|
@ -19,7 +21,6 @@ import { Tooltip } from "@calcom/ui/Tooltip";
|
|||
import TeamAvailabilityModal from "@ee/components/team/availability/TeamAvailabilityModal";
|
||||
|
||||
import useCurrentUserId from "@lib/hooks/useCurrentUserId";
|
||||
import { inferQueryOutput, trpc } from "@lib/trpc";
|
||||
|
||||
import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent";
|
||||
import Avatar from "@components/ui/Avatar";
|
||||
|
@ -39,6 +40,7 @@ export default function MemberListItem(props: Props) {
|
|||
const utils = trpc.useContext();
|
||||
const [showChangeMemberRoleModal, setShowChangeMemberRoleModal] = useState(false);
|
||||
const [showTeamAvailabilityModal, setShowTeamAvailabilityModal] = useState(false);
|
||||
const [showImpersonateModal, setShowImpersonateModal] = useState(false);
|
||||
|
||||
const removeMemberMutation = trpc.useMutation("viewer.teams.removeMember", {
|
||||
async onSuccess() {
|
||||
|
@ -147,6 +149,24 @@ export default function MemberListItem(props: Props) {
|
|||
</Button>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator className="h-px bg-gray-200" />
|
||||
{/* Only show impersonate box if - The user has impersonation enabled,
|
||||
They have accepted the team invite, and it is enabled for this instance */}
|
||||
{!props.member.disableImpersonation &&
|
||||
props.member.accepted &&
|
||||
process.env.NEXT_PUBLIC_TEAM_IMPERSONATION === "true" && (
|
||||
<>
|
||||
<DropdownMenuItem>
|
||||
<Button
|
||||
onClick={() => setShowImpersonateModal(true)}
|
||||
color="minimal"
|
||||
StartIcon={LockClosedIcon}
|
||||
className="w-full flex-shrink-0 font-normal">
|
||||
{t("impersonate")}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator className="h-px bg-gray-200" />
|
||||
</>
|
||||
)}
|
||||
<DropdownMenuItem>
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
|
@ -185,6 +205,39 @@ export default function MemberListItem(props: Props) {
|
|||
onExit={() => setShowChangeMemberRoleModal(false)}
|
||||
/>
|
||||
)}
|
||||
{showImpersonateModal && props.member.username && (
|
||||
<ModalContainer isOpen={showImpersonateModal} onExit={() => setShowImpersonateModal(false)}>
|
||||
<>
|
||||
<div className="mb-4 sm:flex sm:items-start">
|
||||
<div className="text-center sm:text-left">
|
||||
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title">
|
||||
{t("impersonate")}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<form
|
||||
onSubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
await signIn("impersonation-auth", {
|
||||
username: props.member.username,
|
||||
teamId: props.team.id,
|
||||
});
|
||||
}}>
|
||||
<p className="mt-2 text-sm text-gray-500" id="email-description">
|
||||
{t("impersonate_user_tip")}
|
||||
</p>
|
||||
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<Button type="submit" color="primary" className="ltr:ml-2 rtl:mr-2">
|
||||
{t("impersonate")}
|
||||
</Button>
|
||||
<Button type="button" color="secondary" onClick={() => setShowImpersonateModal(false)}>
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
</ModalContainer>
|
||||
)}
|
||||
{showTeamAvailabilityModal && (
|
||||
<ModalContainer
|
||||
wide
|
||||
|
|
|
@ -2,12 +2,11 @@ import { UsersIcon } from "@heroicons/react/outline";
|
|||
import { useRef, useState } from "react";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Button } from "@calcom/ui";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import { Dialog, DialogContent, DialogFooter } from "@calcom/ui/Dialog";
|
||||
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import showToast from "@calcom/lib/notification";
|
||||
|
||||
import { inferQueryOutput, trpc } from "@lib/trpc";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
|
||||
import TeamListItem from "./TeamListItem";
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import Link from "next/link";
|
|||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogTrigger } from "@calcom/ui/Dialog";
|
||||
import Dropdown, {
|
||||
|
@ -25,7 +26,6 @@ import { Tooltip } from "@calcom/ui/Tooltip";
|
|||
|
||||
import classNames from "@lib/classNames";
|
||||
import { getPlaceholderAvatar } from "@lib/getPlaceholderAvatar";
|
||||
import { inferQueryOutput, trpc } from "@lib/trpc";
|
||||
|
||||
import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent";
|
||||
import Avatar from "@components/ui/Avatar";
|
||||
|
|
|
@ -4,13 +4,12 @@ import React, { useRef, useState } from "react";
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { objectKeys } from "@calcom/lib/objectKeys";
|
||||
import { TeamWithMembers } from "@calcom/lib/server/queries/teams";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { TextField } from "@calcom/ui/form/fields";
|
||||
|
||||
import { TeamWithMembers } from "@lib/queries/teams";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import ImageUploader from "@components/ImageUploader";
|
||||
import SettingInputContainer from "@components/ui/SettingInputContainer";
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@ import { useRouter } from "next/router";
|
|||
import React from "react";
|
||||
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { TeamWithMembers } from "@calcom/lib/server/queries/teams";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Dialog, DialogTrigger } from "@calcom/ui/Dialog";
|
||||
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import { TeamWithMembers } from "@lib/queries/teams";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent";
|
||||
import CreateEventTypeButton from "@components/eventtype/CreateEventType";
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { useState } from "react";
|
||||
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import {
|
||||
|
@ -13,7 +14,6 @@ import {
|
|||
} from "@calcom/ui/Dialog";
|
||||
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
interface Props {
|
||||
teamId: number;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
||||
import * as Tooltip from "@radix-ui/react-tooltip";
|
||||
|
||||
import { Maybe } from "@calcom/trpc/server";
|
||||
|
||||
import classNames from "@lib/classNames";
|
||||
import { defaultAvatarSrc } from "@lib/profile";
|
||||
|
||||
import { Maybe } from "@trpc/server";
|
||||
|
||||
export type AvatarProps = {
|
||||
className?: string;
|
||||
size?: number;
|
||||
|
|
|
@ -1,18 +1,31 @@
|
|||
import { PencilIcon } from "@heroicons/react/solid";
|
||||
import { useState } from "react";
|
||||
|
||||
const EditableHeading = ({ title, onChange }: { title: string; onChange: (value: string) => void }) => {
|
||||
const [editIcon, setEditIcon] = useState(true);
|
||||
const EditableHeading = ({
|
||||
title,
|
||||
onChange,
|
||||
placeholder = "",
|
||||
readOnly = false,
|
||||
}: {
|
||||
title: string;
|
||||
onChange?: (value: string) => void;
|
||||
placeholder?: string;
|
||||
readOnly?: boolean;
|
||||
}) => {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const enableEditing = () => !readOnly && setIsEditing(true);
|
||||
return (
|
||||
<div className="group relative cursor-pointer" onClick={() => setEditIcon(false)}>
|
||||
{editIcon ? (
|
||||
<div className="group relative cursor-pointer" onClick={enableEditing}>
|
||||
{!isEditing ? (
|
||||
<>
|
||||
<h1
|
||||
style={{ fontSize: 22, letterSpacing: "-0.0009em" }}
|
||||
className="inline pl-0 text-gray-900 focus:text-black group-hover:text-gray-500">
|
||||
className="inline pl-0 normal-case text-gray-900 focus:text-black group-hover:text-gray-500">
|
||||
{title}
|
||||
</h1>
|
||||
<PencilIcon className="ml-1 -mt-1 inline h-4 w-4 text-gray-700 group-hover:text-gray-500" />
|
||||
{!readOnly ? (
|
||||
<PencilIcon className="ml-1 -mt-1 inline h-4 w-4 text-gray-700 group-hover:text-gray-500" />
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<div style={{ marginBottom: -11 }}>
|
||||
|
@ -22,8 +35,12 @@ const EditableHeading = ({ title, onChange }: { title: string; onChange: (value:
|
|||
style={{ top: -6, fontSize: 22 }}
|
||||
required
|
||||
className="relative h-10 w-full cursor-pointer border-none bg-transparent pl-0 text-gray-900 hover:text-gray-700 focus:text-black focus:outline-none focus:ring-0"
|
||||
placeholder={placeholder}
|
||||
defaultValue={title}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
onBlur={(e) => {
|
||||
setIsEditing(false);
|
||||
onChange && onChange(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -7,15 +7,13 @@ import { fetchUsername } from "@calcom/lib/fetchUsername";
|
|||
import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { User } from "@calcom/prisma/client";
|
||||
import { TRPCClientErrorLike } from "@calcom/trpc/client";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogClose, DialogContent, DialogHeader } from "@calcom/ui/Dialog";
|
||||
import { Input, Label } from "@calcom/ui/form/fields";
|
||||
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import { AppRouter } from "@server/routers/_app";
|
||||
import { TRPCClientErrorLike } from "@trpc/client";
|
||||
|
||||
export enum UsernameChangeStatusEnum {
|
||||
NORMAL = "NORMAL",
|
||||
UPGRADE = "UPGRADE",
|
||||
|
@ -274,16 +272,14 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
|
|||
|
||||
<div className="flex w-full flex-wrap rounded-sm bg-gray-100 py-3 text-sm">
|
||||
<div className="flex-1 px-2">
|
||||
<p className="text-gray-500">
|
||||
{t("current")} {t("username")}
|
||||
</p>
|
||||
<p className="text-gray-500">{t("current_username")}</p>
|
||||
<p className="mt-1" data-testid="current-username">
|
||||
{currentUsername}
|
||||
</p>
|
||||
</div>
|
||||
<div className="ml-6 flex-1">
|
||||
<p className="text-gray-500" data-testid="new-username">
|
||||
{t("new")} {t("username")}
|
||||
{t("new_username")}
|
||||
</p>
|
||||
<p>{inputUsernameValue}</p>
|
||||
</div>
|
||||
|
|
|
@ -5,15 +5,13 @@ import { MutableRefObject, useCallback, useEffect, useState } from "react";
|
|||
|
||||
import { fetchUsername } from "@calcom/lib/fetchUsername";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { TRPCClientErrorLike } from "@calcom/trpc/client";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogClose, DialogContent, DialogHeader } from "@calcom/ui/Dialog";
|
||||
import { Input, Label } from "@calcom/ui/form/fields";
|
||||
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import { AppRouter } from "@server/routers/_app";
|
||||
import { TRPCClientErrorLike } from "@trpc/client";
|
||||
|
||||
interface ICustomUsernameProps {
|
||||
currentUsername: string | undefined;
|
||||
setCurrentUsername: (value: string | undefined) => void;
|
||||
|
@ -144,7 +142,7 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="xs:hidden">
|
||||
<div className="hidden md:inline">
|
||||
<ActionButtons index="desktop" />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -171,16 +169,14 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
|
|||
|
||||
<div className="flex w-full flex-wrap rounded-sm bg-gray-100 py-3 text-sm">
|
||||
<div className="flex-1 px-2">
|
||||
<p className="text-gray-500">
|
||||
{t("current")} {t("username").toLocaleLowerCase()}
|
||||
</p>
|
||||
<p className="text-gray-500">{t("current_username")}</p>
|
||||
<p className="mt-1" data-testid="current-username">
|
||||
{currentUsername}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-gray-500" data-testid="new-username">
|
||||
{t("new")} {t("username").toLocaleLowerCase()}
|
||||
{t("new_username")}
|
||||
</p>
|
||||
<p>{inputUsernameValue}</p>
|
||||
</div>
|
||||
|
|
|
@ -3,14 +3,13 @@ import { Controller, useForm } from "react-hook-form";
|
|||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { Tooltip } from "@calcom/ui";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { DialogFooter } from "@calcom/ui/Dialog";
|
||||
import Switch from "@calcom/ui/Switch";
|
||||
import { FieldsetLegend, Form, InputGroupBox, TextArea, TextField } from "@calcom/ui/form/fields";
|
||||
|
||||
import { trpc } from "@lib/trpc";
|
||||
import { WEBHOOK_TRIGGER_EVENTS } from "@lib/webhooks/constants";
|
||||
import { WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP } from "@lib/webhooks/constants";
|
||||
import customTemplate, { hasTemplateIntegration } from "@lib/webhooks/integrationTemplate";
|
||||
|
||||
import { TWebhook } from "@components/webhook/WebhookListItem";
|
||||
|
@ -19,14 +18,20 @@ import WebhookTestDisclosure from "@components/webhook/WebhookTestDisclosure";
|
|||
export default function WebhookDialogForm(props: {
|
||||
eventTypeId?: number;
|
||||
defaultValues?: TWebhook;
|
||||
app?: string;
|
||||
handleClose: () => void;
|
||||
}) {
|
||||
const { t } = useLocale();
|
||||
const utils = trpc.useContext();
|
||||
const appId = props.app;
|
||||
|
||||
const triggers = !appId
|
||||
? WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP["core"]
|
||||
: WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP[appId as keyof typeof WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP];
|
||||
const {
|
||||
defaultValues = {
|
||||
id: "",
|
||||
eventTriggers: WEBHOOK_TRIGGER_EVENTS,
|
||||
eventTriggers: triggers,
|
||||
subscriberUrl: "",
|
||||
active: true,
|
||||
payloadTemplate: null,
|
||||
|
@ -60,8 +65,8 @@ export default function WebhookDialogForm(props: {
|
|||
form={form}
|
||||
handleSubmit={async (event) => {
|
||||
const e = changeSecret
|
||||
? { ...event, eventTypeId: props.eventTypeId }
|
||||
: { ...event, secret: currentSecret, eventTypeId: props.eventTypeId };
|
||||
? { ...event, eventTypeId: props.eventTypeId, appId }
|
||||
: { ...event, secret: currentSecret, eventTypeId: props.eventTypeId, appId };
|
||||
if (!useCustomPayloadTemplate && event.payloadTemplate) {
|
||||
event.payloadTemplate = null;
|
||||
}
|
||||
|
@ -115,7 +120,7 @@ export default function WebhookDialogForm(props: {
|
|||
<fieldset className="space-y-2">
|
||||
<FieldsetLegend>{t("event_triggers")}</FieldsetLegend>
|
||||
<InputGroupBox className="border-0 bg-gray-50">
|
||||
{WEBHOOK_TRIGGER_EVENTS.map((key) => (
|
||||
{triggers.map((key) => (
|
||||
<Controller
|
||||
key={key}
|
||||
control={form.control}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { PlusIcon } from "@heroicons/react/solid";
|
||||
import { useState } from "react";
|
||||
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogContent } from "@calcom/ui/Dialog";
|
||||
|
||||
import { QueryCell } from "@lib/QueryCell";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import { List } from "@components/List";
|
||||
import { ShellSubHeading } from "@components/Shell";
|
||||
|
@ -17,12 +17,16 @@ export type WebhookListContainerType = {
|
|||
title: string;
|
||||
subtitle: string;
|
||||
eventTypeId?: number;
|
||||
appId?: string;
|
||||
};
|
||||
|
||||
export default function WebhookListContainer(props: WebhookListContainerType) {
|
||||
const query = trpc.useQuery(["viewer.webhook.list", { eventTypeId: props.eventTypeId }], {
|
||||
suspense: true,
|
||||
});
|
||||
const query = trpc.useQuery(
|
||||
["viewer.webhook.list", { eventTypeId: props.eventTypeId, appId: props.appId }],
|
||||
{
|
||||
suspense: true,
|
||||
}
|
||||
);
|
||||
const [newWebhookModal, setNewWebhookModal] = useState(false);
|
||||
const [editModalOpen, setEditModalOpen] = useState(false);
|
||||
const [editing, setEditing] = useState<TWebhook | null>(null);
|
||||
|
@ -66,6 +70,7 @@ export default function WebhookListContainer(props: WebhookListContainerType) {
|
|||
<Dialog open={newWebhookModal} onOpenChange={(isOpen) => !isOpen && setNewWebhookModal(false)}>
|
||||
<DialogContent>
|
||||
<WebhookDialogForm
|
||||
app={props.appId}
|
||||
eventTypeId={props.eventTypeId}
|
||||
handleClose={() => setNewWebhookModal(false)}
|
||||
/>
|
||||
|
@ -76,6 +81,7 @@ export default function WebhookListContainer(props: WebhookListContainerType) {
|
|||
<DialogContent>
|
||||
{editing && (
|
||||
<WebhookDialogForm
|
||||
app={props.appId}
|
||||
key={editing.id}
|
||||
eventTypeId={props.eventTypeId || undefined}
|
||||
handleClose={() => setEditModalOpen(false)}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { PencilAltIcon, TrashIcon } from "@heroicons/react/outline";
|
||||
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogTrigger } from "@calcom/ui/Dialog";
|
||||
import { Tooltip } from "@calcom/ui/Tooltip";
|
||||
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import { inferQueryOutput, trpc } from "@lib/trpc";
|
||||
|
||||
import { ListItem } from "@components/List";
|
||||
import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent";
|
||||
|
|
|
@ -5,11 +5,11 @@ import { useWatch } from "react-hook-form";
|
|||
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { InputGroupBox } from "@calcom/ui/form/fields";
|
||||
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
export default function WebhookTestDisclosure() {
|
||||
const subscriberUrl: string = useWatch({ name: "subscriberUrl" });
|
||||
|
|
|
@ -5,14 +5,13 @@ import { Controller, useForm } from "react-hook-form";
|
|||
import dayjs from "@calcom/dayjs";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { DialogFooter } from "@calcom/ui/Dialog";
|
||||
import Switch from "@calcom/ui/Switch";
|
||||
import { Tooltip } from "@calcom/ui/Tooltip";
|
||||
import { Form, TextField } from "@calcom/ui/form/fields";
|
||||
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import { DatePicker } from "@components/ui/form/DatePicker";
|
||||
|
||||
import LicenseRequired from "../LicenseRequired";
|
||||
|
|
|
@ -2,13 +2,13 @@ import { PlusIcon } from "@heroicons/react/outline";
|
|||
import { useState } from "react";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogContent } from "@calcom/ui/Dialog";
|
||||
import ApiKeyDialogForm from "@ee/components/apiKeys/ApiKeyDialogForm";
|
||||
import ApiKeyListItem, { TApiKeys } from "@ee/components/apiKeys/ApiKeyListItem";
|
||||
|
||||
import { QueryCell } from "@lib/QueryCell";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import { List } from "@components/List";
|
||||
import { ShellSubHeading } from "@components/Shell";
|
||||
|
|
|
@ -4,12 +4,11 @@ import { ExclamationIcon } from "@heroicons/react/solid";
|
|||
import dayjs from "@calcom/dayjs";
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogTrigger } from "@calcom/ui/Dialog";
|
||||
import { Tooltip } from "@calcom/ui/Tooltip";
|
||||
|
||||
import { inferQueryOutput, trpc } from "@lib/trpc";
|
||||
|
||||
import { ListItem } from "@components/List";
|
||||
import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent";
|
||||
import Badge from "@components/ui/Badge";
|
||||
|
|
|
@ -2,13 +2,13 @@ import React, { useEffect, useRef, useState } from "react";
|
|||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogTrigger } from "@calcom/ui/Dialog";
|
||||
import { TextArea } from "@calcom/ui/form/fields";
|
||||
|
||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent";
|
||||
import Badge from "@components/ui/Badge";
|
||||
|
|
|
@ -4,10 +4,10 @@ import { HelpScout, useChat } from "react-live-chat-loader";
|
|||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
|
||||
import classNames from "@lib/classNames";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import ContactMenuItem from "./ContactMenuItem";
|
||||
|
||||
|
|
|
@ -2,10 +2,9 @@ import React, { useState, useEffect } from "react";
|
|||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import LicenseRequired from "@ee/components/LicenseRequired";
|
||||
|
||||
import { inferQueryOutput, trpc } from "@lib/trpc";
|
||||
|
||||
import Avatar from "@components/ui/Avatar";
|
||||
import { DatePicker } from "@components/ui/form/DatePicker";
|
||||
import Select from "@components/ui/form/Select";
|
||||
|
|
|
@ -4,8 +4,7 @@ import { FixedSizeList as List } from "react-window";
|
|||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { CAL_URL } from "@calcom/lib/constants";
|
||||
|
||||
import { inferQueryOutput, trpc } from "@lib/trpc";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
|
||||
import Avatar from "@components/ui/Avatar";
|
||||
import { DatePicker } from "@components/ui/form/DatePicker";
|
||||
|
|
|
@ -3,9 +3,8 @@ import React from "react";
|
|||
import { ITimezone } from "react-timezone-select";
|
||||
|
||||
import { Dayjs } from "@calcom/dayjs";
|
||||
|
||||
import getSlots from "@lib/slots";
|
||||
import { trpc } from "@lib/trpc";
|
||||
import getSlots from "@calcom/lib/slots";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
|
||||
import Loader from "@components/Loader";
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import { z } from "zod";
|
|||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Button } from "@calcom/ui";
|
||||
import { Dialog, DialogClose, DialogContent, DialogTrigger } from "@calcom/ui/Dialog";
|
||||
import { Form, TextField } from "@calcom/ui/form/fields";
|
||||
|
@ -20,7 +21,6 @@ import {
|
|||
} from "@ee/lib/workflows/getOptions";
|
||||
|
||||
import { HttpError } from "@lib/core/http/error";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import PhoneInput from "@components/ui/form/PhoneInput";
|
||||
import Select from "@components/ui/form/Select";
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
import { WorkflowActions, WorkflowTemplates } from "@prisma/client";
|
||||
import { useRouter } from "next/router";
|
||||
import { useState, useEffect, Dispatch, SetStateAction } from "react";
|
||||
import { useState, Dispatch, SetStateAction, useMemo } from "react";
|
||||
import { Controller, UseFormReturn } from "react-hook-form";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Button } from "@calcom/ui";
|
||||
import { Form } from "@calcom/ui/form/fields";
|
||||
import { AddActionDialog } from "@ee/components/workflows/AddActionDialog";
|
||||
import WorkflowStepContainer from "@ee/components/workflows/WorkflowStepContainer";
|
||||
import { Option, FormValues } from "@ee/pages/workflows/[workflow]";
|
||||
import { FormValues } from "@ee/pages/workflows/[workflow]";
|
||||
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import MultiSelectCheckboxes from "@components/ui/form/MultiSelectCheckboxes";
|
||||
import MultiSelectCheckboxes, { Option } from "@components/ui/form/MultiSelectCheckboxes";
|
||||
|
||||
interface Props {
|
||||
form: UseFormReturn<FormValues, any>;
|
||||
form: UseFormReturn<FormValues>;
|
||||
workflowId: number;
|
||||
selectedEventTypes: Option[];
|
||||
setSelectedEventTypes: Dispatch<SetStateAction<Option[]>>;
|
||||
|
@ -29,30 +28,31 @@ export default function WorkflowDetailsPage(props: Props) {
|
|||
const router = useRouter();
|
||||
const utils = trpc.useContext();
|
||||
|
||||
const [evenTypeOptions, setEventTypeOptions] = useState<Option[]>([]);
|
||||
const [isAddActionDialogOpen, setIsAddActionDialogOpen] = useState(false);
|
||||
const [reload, setReload] = useState(false);
|
||||
const [editCounter, setEditCounter] = useState(0);
|
||||
|
||||
const { data, isLoading } = trpc.useQuery(["viewer.eventTypes"]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
let options: Option[] = [];
|
||||
data.eventTypeGroups.forEach((group) => {
|
||||
const eventTypeOptions = group.eventTypes.map((eventType) => {
|
||||
return { value: String(eventType.id), label: eventType.title };
|
||||
});
|
||||
options = [...options, ...eventTypeOptions];
|
||||
});
|
||||
setEventTypeOptions(options);
|
||||
}
|
||||
}, [isLoading]);
|
||||
const eventTypeOptions = useMemo(
|
||||
() =>
|
||||
data?.eventTypeGroups.reduce(
|
||||
(options, group) => [
|
||||
...options,
|
||||
...group.eventTypes.map((eventType) => ({
|
||||
value: String(eventType.id),
|
||||
label: eventType.title,
|
||||
})),
|
||||
],
|
||||
[] as Option[]
|
||||
) || [],
|
||||
[data]
|
||||
);
|
||||
|
||||
const updateMutation = trpc.useMutation("viewer.workflows.update", {
|
||||
onSuccess: async ({ workflow }) => {
|
||||
if (workflow) {
|
||||
await utils.setQueryData(["viewer.workflows.get", { id: +workflow.id }], workflow);
|
||||
utils.setQueryData(["viewer.workflows.get", { id: +workflow.id }], workflow);
|
||||
|
||||
showToast(
|
||||
t("workflow_updated_successfully", {
|
||||
|
@ -74,7 +74,7 @@ export default function WorkflowDetailsPage(props: Props) {
|
|||
const addAction = (action: WorkflowActions, sendTo?: string) => {
|
||||
const steps = form.getValues("steps");
|
||||
const id =
|
||||
steps && steps.length > 0
|
||||
steps?.length > 0
|
||||
? steps.sort((a, b) => {
|
||||
return a.id - b.id;
|
||||
})[0].id - 1
|
||||
|
@ -130,7 +130,7 @@ export default function WorkflowDetailsPage(props: Props) {
|
|||
render={() => {
|
||||
return (
|
||||
<MultiSelectCheckboxes
|
||||
options={evenTypeOptions}
|
||||
options={eventTypeOptions}
|
||||
isLoading={isLoading}
|
||||
setSelected={setSelectedEventTypes}
|
||||
selected={selectedEventTypes}
|
||||
|
|
|
@ -6,13 +6,13 @@ import { useState } from "react";
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { EventType, Workflow, WorkflowsOnEventTypes } from "@calcom/prisma/client";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Button, Tooltip } from "@calcom/ui";
|
||||
import { Dialog } from "@calcom/ui/Dialog";
|
||||
import Dropdown, { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@calcom/ui/Dropdown";
|
||||
import EmptyScreen from "@calcom/ui/EmptyScreen";
|
||||
|
||||
import { HttpError } from "@lib/core/http/error";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent";
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ import { Dispatch, SetStateAction, useState } from "react";
|
|||
import { Controller, UseFormReturn } from "react-hook-form";
|
||||
import PhoneInput from "react-phone-number-input";
|
||||
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Button } from "@calcom/ui";
|
||||
import Dropdown, { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@calcom/ui/Dropdown";
|
||||
import Select from "@calcom/ui/form/Select";
|
||||
|
@ -23,12 +25,9 @@ import {
|
|||
} from "@ee/lib/workflows/getOptions";
|
||||
import { FormValues } from "@ee/pages/workflows/[workflow]";
|
||||
|
||||
import classNames from "@lib/classNames";
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
|
||||
type WorkflowStepProps = {
|
||||
step?: WorkflowStep;
|
||||
form: UseFormReturn<FormValues, any>;
|
||||
form: UseFormReturn<FormValues>;
|
||||
reload?: boolean;
|
||||
setReload?: Dispatch<SetStateAction<boolean>>;
|
||||
editCounter: number;
|
||||
|
|
|
@ -1,66 +1,136 @@
|
|||
import { User } from "@prisma/client";
|
||||
import CredentialsProvider from "next-auth/providers/credentials";
|
||||
import { getSession } from "next-auth/react";
|
||||
|
||||
import { asNumberOrThrow } from "@lib/asStringOrNull";
|
||||
import prisma from "@lib/prisma";
|
||||
|
||||
const auditAndReturnNextUser = async (
|
||||
impersonatedUser: Pick<User, "id" | "username" | "email" | "name" | "role">,
|
||||
impersonatedByUID: number
|
||||
) => {
|
||||
// Log impersonations for audit purposes
|
||||
await prisma.impersonations.create({
|
||||
data: {
|
||||
impersonatedBy: {
|
||||
connect: {
|
||||
id: impersonatedByUID,
|
||||
},
|
||||
},
|
||||
impersonatedUser: {
|
||||
connect: {
|
||||
id: impersonatedUser.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const obj = {
|
||||
id: impersonatedUser.id,
|
||||
username: impersonatedUser.username,
|
||||
email: impersonatedUser.email,
|
||||
name: impersonatedUser.name,
|
||||
role: impersonatedUser.role,
|
||||
impersonatedByUID,
|
||||
};
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
const ImpersonationProvider = CredentialsProvider({
|
||||
id: "impersonation-auth",
|
||||
name: "Impersonation",
|
||||
type: "credentials",
|
||||
credentials: {
|
||||
username: { label: "Username", type: "text " },
|
||||
username: { type: "text" },
|
||||
teamId: { type: "text" },
|
||||
},
|
||||
async authorize(creds, req) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore need to figure out how to correctly type this
|
||||
const session = await getSession({ req });
|
||||
if (session?.user.role !== "ADMIN") {
|
||||
throw new Error("You do not have permission to do this.");
|
||||
}
|
||||
const teamId = creds?.teamId ? asNumberOrThrow(creds.teamId) : undefined;
|
||||
|
||||
if (session?.user.username === creds?.username) {
|
||||
throw new Error("You cannot impersonate yourself.");
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
if (!creds?.username) throw new Error("Username must be present");
|
||||
// If you are an ADMIN we return way before team impersonation logic is executed, so NEXT_PUBLIC_TEAM_IMPERSONATION certainly true
|
||||
if (session?.user.role !== "ADMIN" && process.env.NEXT_PUBLIC_TEAM_IMPERSONATION === "false") {
|
||||
throw new Error("You do not have permission to do this.");
|
||||
}
|
||||
|
||||
// Get user who is being impersonated
|
||||
const impersonatedUser = await prisma.user.findUnique({
|
||||
where: {
|
||||
username: creds?.username,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new Error("This user does not exist");
|
||||
}
|
||||
|
||||
if (user.disableImpersonation) {
|
||||
throw new Error("This user has disabled Impersonation.");
|
||||
}
|
||||
|
||||
// Log impersonations for audit purposes
|
||||
await prisma.impersonations.create({
|
||||
data: {
|
||||
impersonatedBy: {
|
||||
connect: {
|
||||
id: session.user.id,
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
role: true,
|
||||
name: true,
|
||||
email: true,
|
||||
disableImpersonation: true,
|
||||
teams: {
|
||||
where: {
|
||||
disableImpersonation: false, // Ensure they have impersonation enabled
|
||||
accepted: true, // Ensure they are apart of the team and not just invited.
|
||||
team: {
|
||||
id: teamId, // Bring back only the right team
|
||||
},
|
||||
},
|
||||
},
|
||||
impersonatedUser: {
|
||||
connect: {
|
||||
id: user.id,
|
||||
select: {
|
||||
teamId: true,
|
||||
disableImpersonation: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const obj = {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
role: user.role,
|
||||
impersonatedByUID: session?.user.id,
|
||||
};
|
||||
return obj;
|
||||
// Check if impersonating is allowed for this user
|
||||
if (!impersonatedUser) {
|
||||
throw new Error("This user does not exist");
|
||||
}
|
||||
|
||||
if (session?.user.role === "ADMIN") {
|
||||
if (impersonatedUser.disableImpersonation) {
|
||||
throw new Error("This user has disabled Impersonation.");
|
||||
}
|
||||
return auditAndReturnNextUser(impersonatedUser, session?.user.id as number);
|
||||
}
|
||||
|
||||
// Check session
|
||||
const sessionUserFromDb = await prisma.user.findUnique({
|
||||
where: {
|
||||
id: session?.user.id,
|
||||
},
|
||||
include: {
|
||||
teams: {
|
||||
where: {
|
||||
AND: [
|
||||
{
|
||||
role: {
|
||||
in: ["ADMIN", "OWNER"],
|
||||
},
|
||||
},
|
||||
{
|
||||
team: {
|
||||
id: teamId,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (sessionUserFromDb?.teams.length === 0 || impersonatedUser.teams.length === 0) {
|
||||
throw new Error("You do not have permission to do this.");
|
||||
}
|
||||
|
||||
return auditAndReturnNextUser(impersonatedUser, session?.user.id as number);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,181 +0,0 @@
|
|||
import { PaymentType, Prisma } from "@prisma/client";
|
||||
import Stripe from "stripe";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { z } from "zod";
|
||||
|
||||
import getAppKeysFromSlug from "@calcom/app-store/_utils/getAppKeysFromSlug";
|
||||
import { sendAwaitingPaymentEmail, sendOrganizerPaymentRefundFailedEmail } from "@calcom/emails";
|
||||
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { createPaymentLink } from "@calcom/stripe/client";
|
||||
import stripe, { PaymentData } from "@calcom/stripe/server";
|
||||
import { CalendarEvent } from "@calcom/types/Calendar";
|
||||
|
||||
const stripeKeysSchema = z.object({
|
||||
payment_fee_fixed: z.number(),
|
||||
payment_fee_percentage: z.number(),
|
||||
});
|
||||
|
||||
const stripeCredentialSchema = z.object({
|
||||
stripe_user_id: z.string(),
|
||||
stripe_publishable_key: z.string(),
|
||||
});
|
||||
|
||||
export async function handlePayment(
|
||||
evt: CalendarEvent,
|
||||
selectedEventType: {
|
||||
price: number;
|
||||
currency: string;
|
||||
},
|
||||
stripeCredential: { key: Prisma.JsonValue },
|
||||
booking: {
|
||||
user: { email: string | null; name: string | null; timeZone: string } | null;
|
||||
id: number;
|
||||
startTime: { toISOString: () => string };
|
||||
uid: string;
|
||||
}
|
||||
) {
|
||||
const appKeys = await getAppKeysFromSlug("stripe");
|
||||
const { payment_fee_fixed, payment_fee_percentage } = stripeKeysSchema.parse(appKeys);
|
||||
|
||||
const paymentFee = Math.round(selectedEventType.price * payment_fee_percentage + payment_fee_fixed);
|
||||
const { stripe_user_id, stripe_publishable_key } = stripeCredentialSchema.parse(stripeCredential.key);
|
||||
|
||||
const params: Stripe.PaymentIntentCreateParams = {
|
||||
amount: selectedEventType.price,
|
||||
currency: selectedEventType.currency,
|
||||
payment_method_types: ["card"],
|
||||
application_fee_amount: paymentFee,
|
||||
};
|
||||
|
||||
const paymentIntent = await stripe.paymentIntents.create(params, { stripeAccount: stripe_user_id });
|
||||
|
||||
const payment = await prisma.payment.create({
|
||||
data: {
|
||||
type: PaymentType.STRIPE,
|
||||
uid: uuidv4(),
|
||||
booking: {
|
||||
connect: {
|
||||
id: booking.id,
|
||||
},
|
||||
},
|
||||
amount: selectedEventType.price,
|
||||
fee: paymentFee,
|
||||
currency: selectedEventType.currency,
|
||||
success: false,
|
||||
refunded: false,
|
||||
data: Object.assign({}, paymentIntent, {
|
||||
stripe_publishable_key,
|
||||
stripeAccount: stripe_user_id,
|
||||
}) /* We should treat this */ as PaymentData /* but Prisma doesn't know how to handle it, so it we treat it */ as unknown /* and then */ as Prisma.InputJsonValue,
|
||||
externalId: paymentIntent.id,
|
||||
},
|
||||
});
|
||||
|
||||
await sendAwaitingPaymentEmail({
|
||||
...evt,
|
||||
paymentInfo: {
|
||||
link: createPaymentLink({
|
||||
paymentUid: payment.uid,
|
||||
name: booking.user?.name,
|
||||
email: booking.user?.email,
|
||||
date: booking.startTime.toISOString(),
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
return payment;
|
||||
}
|
||||
|
||||
export async function refund(
|
||||
booking: {
|
||||
id: number;
|
||||
uid: string;
|
||||
startTime: Date;
|
||||
payment: {
|
||||
id: number;
|
||||
success: boolean;
|
||||
refunded: boolean;
|
||||
externalId: string;
|
||||
data: Prisma.JsonValue;
|
||||
type: PaymentType;
|
||||
}[];
|
||||
},
|
||||
calEvent: CalendarEvent
|
||||
) {
|
||||
try {
|
||||
const payment = booking.payment.find((e) => e.success && !e.refunded);
|
||||
if (!payment) return;
|
||||
|
||||
if (payment.type !== PaymentType.STRIPE) {
|
||||
await handleRefundError({
|
||||
event: calEvent,
|
||||
reason: "cannot refund non Stripe payment",
|
||||
paymentId: "unknown",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const refund = await stripe.refunds.create(
|
||||
{
|
||||
payment_intent: payment.externalId,
|
||||
},
|
||||
{ stripeAccount: (payment.data as unknown as PaymentData)["stripeAccount"] }
|
||||
);
|
||||
|
||||
if (!refund || refund.status === "failed") {
|
||||
await handleRefundError({
|
||||
event: calEvent,
|
||||
reason: refund?.failure_reason || "unknown",
|
||||
paymentId: payment.externalId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await prisma.payment.update({
|
||||
where: {
|
||||
id: payment.id,
|
||||
},
|
||||
data: {
|
||||
refunded: true,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
const err = getErrorFromUnknown(e);
|
||||
console.error(err, "Refund failed");
|
||||
await handleRefundError({
|
||||
event: calEvent,
|
||||
reason: err.message || "unknown",
|
||||
paymentId: "unknown",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const closePayments = async (paymentIntentId: string, stripeAccount: string) => {
|
||||
try {
|
||||
// Expire all current sessions
|
||||
const sessions = await stripe.checkout.sessions.list(
|
||||
{
|
||||
payment_intent: paymentIntentId,
|
||||
},
|
||||
{ stripeAccount }
|
||||
);
|
||||
for (const session of sessions.data) {
|
||||
await stripe.checkout.sessions.expire(session.id, { stripeAccount });
|
||||
}
|
||||
// Then cancel the payment intent
|
||||
await stripe.paymentIntents.cancel(paymentIntentId, { stripeAccount });
|
||||
return;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
async function handleRefundError(opts: { event: CalendarEvent; reason: string; paymentId: string }) {
|
||||
console.error(`refund failed: ${opts.reason} for booking '${opts.event.uid}'`);
|
||||
await sendOrganizerPaymentRefundFailedEmail({
|
||||
...opts.event,
|
||||
paymentInfo: { reason: opts.reason, id: opts.paymentId },
|
||||
});
|
||||
}
|
|
@ -9,7 +9,6 @@ import client from "@sendgrid/client";
|
|||
import sgMail from "@sendgrid/mail";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { sendWorkflowReminderEmail } from "@calcom/emails";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { BookingInfo, timeUnitLowerCase } from "@ee/lib/workflows/reminders/smsReminderManager";
|
||||
import emailReminderTemplate from "@ee/lib/workflows/reminders/templates/emailReminderTemplate";
|
||||
|
@ -46,7 +45,10 @@ export const scheduleEmailReminder = async (
|
|||
const scheduledDate =
|
||||
timeBefore.time && timeUnit ? dayjs(startTime).subtract(timeBefore.time, timeUnit) : null;
|
||||
|
||||
if (!process.env.SENDGRID_API_KEY || !process.env.SENDGRID_EMAIL) return;
|
||||
if (!process.env.SENDGRID_API_KEY || !process.env.SENDGRID_EMAIL) {
|
||||
console.error("Sendgrid credentials are missing from the .env file");
|
||||
return;
|
||||
}
|
||||
|
||||
const batchIdResponse = await client.request({
|
||||
url: "/v3/mail/batch",
|
||||
|
@ -57,11 +59,17 @@ export const scheduleEmailReminder = async (
|
|||
const attendeeName = action === WorkflowActions.EMAIL_HOST ? evt.attendees[0].name : evt.organizer.name;
|
||||
const timeZone = action === WorkflowActions.EMAIL_HOST ? evt.organizer.timeZone : evt.attendees[0].timeZone;
|
||||
|
||||
let emailContent = {
|
||||
emailSubject,
|
||||
emailBody: {
|
||||
text: emailBody,
|
||||
html: `<body style="white-space: pre-wrap;">${emailBody}</body>`,
|
||||
},
|
||||
};
|
||||
|
||||
switch (template) {
|
||||
case WorkflowTemplates.REMINDER:
|
||||
const emailTemplate = emailReminderTemplate(startTime, evt.title, timeZone, attendeeName, name);
|
||||
emailSubject = emailTemplate.subject;
|
||||
emailBody = emailTemplate.body;
|
||||
emailContent = emailReminderTemplate(startTime, evt.title, timeZone, attendeeName, name);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -70,7 +78,14 @@ export const scheduleEmailReminder = async (
|
|||
triggerEvent === WorkflowTriggerEvents.EVENT_CANCELLED
|
||||
) {
|
||||
try {
|
||||
await sendWorkflowReminderEmail(evt, sendTo, emailSubject, emailBody);
|
||||
await sgMail.send({
|
||||
to: sendTo,
|
||||
from: senderEmail,
|
||||
subject: emailContent.emailSubject,
|
||||
text: emailContent.emailBody.text,
|
||||
html: emailContent.emailBody.html,
|
||||
batchId: batchIdResponse[1].batch_id,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Error sending Email");
|
||||
}
|
||||
|
@ -85,13 +100,9 @@ export const scheduleEmailReminder = async (
|
|||
await sgMail.send({
|
||||
to: sendTo,
|
||||
from: senderEmail,
|
||||
subject: emailSubject,
|
||||
content: [
|
||||
{
|
||||
type: "text/html",
|
||||
value: emailBody,
|
||||
},
|
||||
],
|
||||
subject: emailContent.emailSubject,
|
||||
text: emailContent.emailBody.text,
|
||||
html: emailContent.emailBody.html,
|
||||
batchId: batchIdResponse[1].batch_id,
|
||||
sendAt: scheduledDate.unix(),
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
WorkflowTemplates,
|
||||
WorkflowActions,
|
||||
WorkflowMethods,
|
||||
} from "@prisma/client/";
|
||||
} from "@prisma/client";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
|
|
@ -7,19 +7,25 @@ const emailReminderTemplate = (
|
|||
attendee: string,
|
||||
name: string
|
||||
) => {
|
||||
const templateSubject = `Reminder: ${eventName} at ${dayjs(startTime)
|
||||
const emailSubject = `Reminder: ${eventName} on ${dayjs(startTime)
|
||||
.tz(timeZone)
|
||||
.format("YYYY MMM D h:mmA")}`;
|
||||
.format("YYYY MMM D")} at ${dayjs(startTime).tz(timeZone).format("h:mmA")} ${timeZone}.`;
|
||||
|
||||
const templateBody = `Hi ${name},\n\nThis is a reminder that your meeting (${eventName}) with ${attendee} is on ${dayjs(
|
||||
const templateBodyText = `Hi ${name}, this is a reminder that your meeting (${eventName}) with ${attendee} is on ${dayjs(
|
||||
startTime
|
||||
)
|
||||
.tz(timeZone)
|
||||
.format("YYYY MMM D")} at ${dayjs(startTime).tz(timeZone).format("h:mmA")} ${timeZone}.`;
|
||||
|
||||
const emailContent = { subject: templateSubject, body: templateBody };
|
||||
const templateBodyHtml = `<body>Hi ${name},<br><br>This is a reminder that your meeting (${eventName}) with ${attendee} is on ${dayjs(
|
||||
startTime
|
||||
)
|
||||
.tz(timeZone)
|
||||
.format("YYYY MMM D")} at ${dayjs(startTime).tz(timeZone).format("h:mmA")} ${timeZone}.<body>`;
|
||||
|
||||
return emailContent;
|
||||
const emailBody = { text: templateBodyText, html: templateBodyHtml };
|
||||
|
||||
return { emailSubject, emailBody };
|
||||
};
|
||||
|
||||
export default emailReminderTemplate;
|
||||
|
|
|
@ -72,11 +72,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||
? reminder.booking?.user?.email
|
||||
: reminder.booking?.attendees[0].email;
|
||||
|
||||
let emailTemplate = {
|
||||
subject: reminder.workflowStep.emailSubject || "",
|
||||
body: reminder.workflowStep.reminderBody || "",
|
||||
};
|
||||
|
||||
const name =
|
||||
reminder.workflowStep.action === WorkflowActions.EMAIL_ATTENDEE
|
||||
? reminder.booking?.attendees[0].name
|
||||
|
@ -92,9 +87,17 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||
? reminder.booking?.attendees[0].timeZone
|
||||
: reminder.booking?.user?.timeZone;
|
||||
|
||||
let emailContent = {
|
||||
emailSubject: reminder.workflowStep.emailSubject || "",
|
||||
emailBody: {
|
||||
text: reminder.workflowStep.reminderBody || "",
|
||||
html: `<body style="white-space: pre-wrap;">${reminder.workflowStep.reminderBody || ""}</body>`,
|
||||
},
|
||||
};
|
||||
|
||||
switch (reminder.workflowStep.template) {
|
||||
case WorkflowTemplates.REMINDER:
|
||||
emailTemplate = emailReminderTemplate(
|
||||
emailContent = emailReminderTemplate(
|
||||
reminder.booking?.startTime.toISOString() || "",
|
||||
reminder.booking?.eventType?.title || "",
|
||||
timeZone || "",
|
||||
|
@ -103,17 +106,13 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||
);
|
||||
break;
|
||||
}
|
||||
if (emailTemplate.subject.length > 0 && emailTemplate.body.length > 0 && sendTo) {
|
||||
if (emailContent.emailSubject.length > 0 && emailContent.emailBody.text.length > 0 && sendTo) {
|
||||
await sgMail.send({
|
||||
to: sendTo,
|
||||
from: senderEmail,
|
||||
subject: emailTemplate.subject,
|
||||
content: [
|
||||
{
|
||||
type: "text/html",
|
||||
value: emailTemplate.body,
|
||||
},
|
||||
],
|
||||
subject: emailContent.emailSubject,
|
||||
text: emailContent.emailBody.text,
|
||||
html: emailContent.emailBody.html,
|
||||
batchId: batchIdResponse[1].batch_id,
|
||||
sendAt: dayjs(reminder.scheduledDate).unix(),
|
||||
});
|
||||
|
|
|
@ -2,13 +2,13 @@ import { useRouter } from "next/router";
|
|||
import { useMemo, useState } from "react";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import LicenseRequired from "@ee/components/LicenseRequired";
|
||||
import TeamAvailabilityScreen from "@ee/components/team/availability/TeamAvailabilityScreen";
|
||||
|
||||
import { getPlaceholderAvatar } from "@lib/getPlaceholderAvatar";
|
||||
import useMeQuery from "@lib/hooks/useMeQuery";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import Loader from "@components/Loader";
|
||||
import Shell from "@components/Shell";
|
||||
|
|
|
@ -9,6 +9,7 @@ import { useForm } from "react-hook-form";
|
|||
import { z } from "zod";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import Loader from "@calcom/ui/Loader";
|
||||
import LicenseRequired from "@ee/components/LicenseRequired";
|
||||
|
@ -21,14 +22,9 @@ import {
|
|||
} from "@ee/lib/workflows/constants";
|
||||
|
||||
import useMeQuery from "@lib/hooks/useMeQuery";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import Shell from "@components/Shell";
|
||||
|
||||
export type Option = {
|
||||
value: string;
|
||||
label: string;
|
||||
};
|
||||
import { Option } from "@components/ui/form/MultiSelectCheckboxes";
|
||||
|
||||
export type FormValues = {
|
||||
name: string;
|
||||
|
@ -39,6 +35,30 @@ export type FormValues = {
|
|||
timeUnit?: TimeUnit;
|
||||
};
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z.string(),
|
||||
activeOn: z.object({ value: z.string(), label: z.string() }).array(),
|
||||
trigger: z.enum(WORKFLOW_TRIGGER_EVENTS),
|
||||
time: z.number().gte(0).optional(),
|
||||
timeUnit: z.enum(TIME_UNIT).optional(),
|
||||
steps: z
|
||||
.object({
|
||||
id: z.number(),
|
||||
stepNumber: z.number(),
|
||||
action: z.enum(WORKFLOW_ACTIONS),
|
||||
workflowId: z.number(),
|
||||
reminderBody: z.string().optional().nullable(),
|
||||
emailSubject: z.string().optional().nullable(),
|
||||
template: z.enum(WORKFLOW_TEMPLATES),
|
||||
sendTo: z
|
||||
.string()
|
||||
.refine((val) => isValidPhoneNumber(val))
|
||||
.optional()
|
||||
.nullable(),
|
||||
})
|
||||
.array(),
|
||||
});
|
||||
|
||||
function WorkflowPage() {
|
||||
const { t } = useLocale();
|
||||
const session = useSession();
|
||||
|
@ -50,30 +70,6 @@ function WorkflowPage() {
|
|||
const [selectedEventTypes, setSelectedEventTypes] = useState<Option[]>([]);
|
||||
const [isAllDataLoaded, setIsAllDataLoaded] = useState(false);
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z.string(),
|
||||
activeOn: z.object({ value: z.string(), label: z.string() }).array(),
|
||||
trigger: z.enum(WORKFLOW_TRIGGER_EVENTS),
|
||||
time: z.number().gte(0).optional(),
|
||||
timeUnit: z.enum(TIME_UNIT).optional(),
|
||||
steps: z
|
||||
.object({
|
||||
id: z.number(),
|
||||
stepNumber: z.number(),
|
||||
action: z.enum(WORKFLOW_ACTIONS),
|
||||
workflowId: z.number(),
|
||||
reminderBody: z.string().optional().nullable(),
|
||||
emailSubject: z.string().optional().nullable(),
|
||||
template: z.enum(WORKFLOW_TEMPLATES),
|
||||
sendTo: z
|
||||
.string()
|
||||
.refine((val) => isValidPhoneNumber(val))
|
||||
.optional()
|
||||
.nullable(),
|
||||
})
|
||||
.array(),
|
||||
});
|
||||
|
||||
const form = useForm<FormValues>({
|
||||
resolver: zodResolver(formSchema),
|
||||
});
|
||||
|
@ -82,6 +78,8 @@ function WorkflowPage() {
|
|||
|
||||
const {
|
||||
data: workflow,
|
||||
isError,
|
||||
error,
|
||||
isLoading,
|
||||
dataUpdatedAt,
|
||||
} = trpc.useQuery([
|
||||
|
@ -113,15 +111,12 @@ function WorkflowPage() {
|
|||
}
|
||||
}, [dataUpdatedAt]);
|
||||
|
||||
if (isLoading) {
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Shell
|
||||
title="Title"
|
||||
heading={
|
||||
session.data?.hasValidLicense && (
|
||||
session.data?.hasValidLicense &&
|
||||
isAllDataLoaded && (
|
||||
<div className="group relative cursor-pointer" onClick={() => setEditIcon(false)}>
|
||||
{editIcon ? (
|
||||
<>
|
||||
|
@ -160,15 +155,21 @@ function WorkflowPage() {
|
|||
<Alert className="border " severity="warning" title={t("pro_feature_workflows")} />
|
||||
) : (
|
||||
<>
|
||||
{isAllDataLoaded ? (
|
||||
<WorkflowDetailsPage
|
||||
form={form}
|
||||
workflowId={+workflowId}
|
||||
selectedEventTypes={selectedEventTypes}
|
||||
setSelectedEventTypes={setSelectedEventTypes}
|
||||
/>
|
||||
{!isError ? (
|
||||
<>
|
||||
{isAllDataLoaded ? (
|
||||
<WorkflowDetailsPage
|
||||
form={form}
|
||||
workflowId={+workflowId}
|
||||
selectedEventTypes={selectedEventTypes}
|
||||
setSelectedEventTypes={setSelectedEventTypes}
|
||||
/>
|
||||
) : (
|
||||
<Loader />
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Loader />
|
||||
<Alert severity="error" title="Something went wrong" message={error.message} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { useSession } from "next-auth/react";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import LicenseRequired from "@ee/components/LicenseRequired";
|
||||
import { NewWorkflowButton } from "@ee/components/workflows/NewWorkflowButton";
|
||||
import WorkflowList from "@ee/components/workflows/WorkflowListPage";
|
||||
|
||||
import useMeQuery from "@lib/hooks/useMeQuery";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import Loader from "@components/Loader";
|
||||
import Shell from "@components/Shell";
|
||||
|
@ -26,7 +26,7 @@ function WorkflowsPage() {
|
|||
<Shell
|
||||
heading={t("workflows")}
|
||||
subtitle={t("workflows_to_automate_notifications")}
|
||||
CTA={session.data?.hasValidLicense ? <NewWorkflowButton /> : <></>}>
|
||||
CTA={session.data?.hasValidLicense && !isFreeUser ? <NewWorkflowButton /> : <></>}>
|
||||
<LicenseRequired>
|
||||
{isLoading ? (
|
||||
<Loader />
|
||||
|
|
|
@ -3,7 +3,7 @@ import type { Config } from "@jest/types";
|
|||
const config: Config.InitialOptions = {
|
||||
verbose: true,
|
||||
roots: ["<rootDir>"],
|
||||
testMatch: ["**/tests/**/*.+(ts|tsx|js)", "**/?(*.)+(spec|test).+(ts|tsx|js)"],
|
||||
testMatch: ["**/test/lib/**/*.(spec|test).(ts|tsx|js)"],
|
||||
testPathIgnorePatterns: ["<rootDir>/.next", "<rootDir>/playwright/"],
|
||||
transform: {
|
||||
"^.+\\.(js|jsx|ts|tsx)$": ["babel-jest", { presets: ["next/babel"] }],
|
||||
|
|
|
@ -8,22 +8,19 @@ import {
|
|||
UseQueryResult,
|
||||
} from "react-query";
|
||||
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import Loader from "@components/Loader";
|
||||
|
||||
import type { AppRouter } from "@server/routers/_app";
|
||||
import type { TRPCClientErrorLike } from "@trpc/client";
|
||||
import type { UseTRPCQueryOptions } from "@trpc/react";
|
||||
// import type { inferProcedures } from "@trpc/react/src/createReactQueryHooks";
|
||||
import type { TRPCClientErrorLike } from "@calcom/trpc/client";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import type { UseTRPCQueryOptions } from "@calcom/trpc/react";
|
||||
import type {
|
||||
inferHandlerInput,
|
||||
inferProcedureInput,
|
||||
inferProcedureOutput,
|
||||
ProcedureRecord,
|
||||
} from "@trpc/server";
|
||||
} from "@calcom/trpc/server";
|
||||
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
|
||||
import Loader from "@components/Loader";
|
||||
|
||||
type ErrorLike = {
|
||||
message: string;
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
import { SessionProvider } from "next-auth/react";
|
||||
import { appWithTranslation } from "next-i18next";
|
||||
import type { AppProps as NextAppProps, AppProps as NextJsAppProps } from "next/app";
|
||||
import { ComponentProps, ReactNode, useMemo } from "react";
|
||||
import { ComponentProps, ReactNode } from "react";
|
||||
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import DynamicHelpscoutProvider from "@ee/lib/helpscout/providerDynamic";
|
||||
import DynamicIntercomProvider from "@ee/lib/intercom/providerDynamic";
|
||||
|
||||
import usePublicPage from "@lib/hooks/usePublicPage";
|
||||
|
||||
import { trpc } from "./trpc";
|
||||
|
||||
const I18nextAdapter = appWithTranslation<NextJsAppProps & { children: React.ReactNode }>(({ children }) => (
|
||||
<>{children}</>
|
||||
));
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import parser from "accept-language-parser";
|
||||
import { IncomingMessage } from "http";
|
||||
|
||||
import { Maybe } from "@calcom/trpc/server";
|
||||
|
||||
import { getSession } from "@lib/auth";
|
||||
import prisma from "@lib/prisma";
|
||||
|
||||
import { Maybe } from "@trpc/server";
|
||||
|
||||
import { i18n } from "../../../next-i18next.config";
|
||||
|
||||
export function getLocaleFromHeaders(req: IncomingMessage): string {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { trpc } from "../trpc";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
|
||||
export function useMeQuery() {
|
||||
const meQuery = trpc.useQuery(["viewer.me"], {
|
||||
|
|
|
@ -2,8 +2,7 @@ import Head from "next/head";
|
|||
import { useEffect, useState } from "react";
|
||||
|
||||
import { useEmbedTheme } from "@calcom/embed-core/embed-iframe";
|
||||
|
||||
import { Maybe } from "@trpc/server";
|
||||
import { Maybe } from "@calcom/trpc/server";
|
||||
|
||||
// This method is stringified and executed only on client. So,
|
||||
// - Pass all the params explicitly to this method. Don't use closure
|
||||
|
|
|
@ -1,41 +1,3 @@
|
|||
import { EventType, PeriodType } from "@prisma/client";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
|
||||
function isOutOfBounds(
|
||||
time: dayjs.ConfigType,
|
||||
{
|
||||
periodType,
|
||||
periodDays,
|
||||
periodCountCalendarDays,
|
||||
periodStartDate,
|
||||
periodEndDate,
|
||||
}: Pick<
|
||||
EventType,
|
||||
"periodType" | "periodDays" | "periodCountCalendarDays" | "periodStartDate" | "periodEndDate"
|
||||
>
|
||||
) {
|
||||
const date = dayjs(time);
|
||||
periodDays = periodDays || 0;
|
||||
|
||||
switch (periodType) {
|
||||
case PeriodType.ROLLING: {
|
||||
const periodRollingEndDay = periodCountCalendarDays
|
||||
? dayjs().utcOffset(date.utcOffset()).add(periodDays, "days").endOf("day")
|
||||
: dayjs().utcOffset(date.utcOffset()).businessDaysAdd(periodDays).endOf("day");
|
||||
return date.endOf("day").isAfter(periodRollingEndDay);
|
||||
}
|
||||
|
||||
case PeriodType.RANGE: {
|
||||
const periodRangeStartDay = dayjs(periodStartDate).utcOffset(date.utcOffset()).endOf("day");
|
||||
const periodRangeEndDay = dayjs(periodEndDate).utcOffset(date.utcOffset()).endOf("day");
|
||||
return date.endOf("day").isBefore(periodRangeStartDay) || date.endOf("day").isAfter(periodRangeEndDay);
|
||||
}
|
||||
|
||||
case PeriodType.UNLIMITED:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export default isOutOfBounds;
|
||||
/* Prefer import from `@calcom/lib/isOutOfBounds` */
|
||||
export * from "@calcom/lib/isOutOfBounds";
|
||||
export { default } from "@calcom/lib/isOutOfBounds";
|
||||
|
|
|
@ -2,10 +2,10 @@ import { I18n } from "next-i18next";
|
|||
import { RRule } from "rrule";
|
||||
|
||||
import dayjs, { Dayjs } from "@calcom/dayjs";
|
||||
import { inferQueryOutput } from "@calcom/trpc/react";
|
||||
import { RecurringEvent } from "@calcom/types/Calendar";
|
||||
|
||||
import { detectBrowserTimeFormat } from "@lib/timeFormat";
|
||||
import { inferQueryOutput } from "@lib/trpc";
|
||||
|
||||
import { parseZone } from "./parseZone";
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
import { BASE_URL } from "@lib/config/constants";
|
||||
import { TRPCError } from "@calcom/trpc/server";
|
||||
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { BASE_URL } from "@lib/config/constants";
|
||||
|
||||
export const samlDatabaseUrl = process.env.SAML_DATABASE_URL || "";
|
||||
export const samlLoginUrl = BASE_URL;
|
||||
|
|
|
@ -1,114 +1,3 @@
|
|||
import dayjs, { Dayjs } from "@calcom/dayjs";
|
||||
|
||||
import { getWorkingHours } from "./availability";
|
||||
import { WorkingHours } from "./types/schedule";
|
||||
|
||||
export type GetSlots = {
|
||||
inviteeDate: Dayjs;
|
||||
frequency: number;
|
||||
workingHours: WorkingHours[];
|
||||
minimumBookingNotice: number;
|
||||
eventLength: number;
|
||||
};
|
||||
export type WorkingHoursTimeFrame = { startTime: number; endTime: number };
|
||||
|
||||
const splitAvailableTime = (
|
||||
startTimeMinutes: number,
|
||||
endTimeMinutes: number,
|
||||
frequency: number,
|
||||
eventLength: number
|
||||
): Array<WorkingHoursTimeFrame> => {
|
||||
let initialTime = startTimeMinutes;
|
||||
const finalizationTime = endTimeMinutes;
|
||||
const result = [] as Array<WorkingHoursTimeFrame>;
|
||||
while (initialTime < finalizationTime) {
|
||||
const periodTime = initialTime + frequency;
|
||||
const slotEndTime = initialTime + eventLength;
|
||||
/*
|
||||
check if the slot end time surpasses availability end time of the user
|
||||
1 minute is added to round up the hour mark so that end of the slot is considered in the check instead of x9
|
||||
eg: if finalization time is 11:59, slotEndTime is 12:00, we ideally want the slot to be available
|
||||
*/
|
||||
if (slotEndTime <= finalizationTime + 1) result.push({ startTime: initialTime, endTime: periodTime });
|
||||
initialTime += frequency;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const getSlots = ({ inviteeDate, frequency, minimumBookingNotice, workingHours, eventLength }: GetSlots) => {
|
||||
// current date in invitee tz
|
||||
const startDate = dayjs().add(minimumBookingNotice, "minute");
|
||||
const startOfDay = dayjs.utc().startOf("day");
|
||||
const startOfInviteeDay = inviteeDate.startOf("day");
|
||||
// checks if the start date is in the past
|
||||
|
||||
/**
|
||||
* TODO: change "day" for "hour" to stop displaying 1 day before today
|
||||
* This is displaying a day as available as sometimes difference between two dates is < 24 hrs.
|
||||
* But when doing timezones an available day for an owner can be 2 days available in other users tz.
|
||||
*
|
||||
* */
|
||||
if (inviteeDate.isBefore(startDate, "day")) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const localWorkingHours = getWorkingHours(
|
||||
{ utcOffset: -inviteeDate.utcOffset() },
|
||||
workingHours.map((schedule) => ({
|
||||
days: schedule.days,
|
||||
startTime: startOfDay.add(schedule.startTime, "minute"),
|
||||
endTime: startOfDay.add(schedule.endTime, "minute"),
|
||||
}))
|
||||
).filter((hours) => hours.days.includes(inviteeDate.day()));
|
||||
|
||||
const slots: Dayjs[] = [];
|
||||
|
||||
const slotsTimeFrameAvailable = [] as Array<WorkingHoursTimeFrame>;
|
||||
// Here we split working hour in chunks for every frequency available that can fit in whole working hours
|
||||
const computedLocalWorkingHours: WorkingHoursTimeFrame[] = [];
|
||||
let tempComputeTimeFrame: WorkingHoursTimeFrame | undefined;
|
||||
const computeLength = localWorkingHours.length - 1;
|
||||
const makeTimeFrame = (item: typeof localWorkingHours[0]): WorkingHoursTimeFrame => ({
|
||||
startTime: item.startTime,
|
||||
endTime: item.endTime,
|
||||
});
|
||||
localWorkingHours.forEach((item, index) => {
|
||||
if (!tempComputeTimeFrame) {
|
||||
tempComputeTimeFrame = makeTimeFrame(item);
|
||||
} else {
|
||||
// please check the comment in splitAvailableTime func for the added 1 minute
|
||||
if (tempComputeTimeFrame.endTime + 1 === item.startTime) {
|
||||
// to deal with time that across the day, e.g. from 11:59 to to 12:01
|
||||
tempComputeTimeFrame.endTime = item.endTime;
|
||||
} else {
|
||||
computedLocalWorkingHours.push(tempComputeTimeFrame);
|
||||
tempComputeTimeFrame = makeTimeFrame(item);
|
||||
}
|
||||
}
|
||||
if (index == computeLength) {
|
||||
computedLocalWorkingHours.push(tempComputeTimeFrame);
|
||||
}
|
||||
});
|
||||
computedLocalWorkingHours.forEach((item) => {
|
||||
slotsTimeFrameAvailable.push(...splitAvailableTime(item.startTime, item.endTime, frequency, eventLength));
|
||||
});
|
||||
|
||||
slotsTimeFrameAvailable.forEach((item) => {
|
||||
const slot = startOfInviteeDay.add(item.startTime, "minute");
|
||||
// Validating slot its not on the past
|
||||
if (!slot.isBefore(startDate)) {
|
||||
slots.push(slot);
|
||||
}
|
||||
});
|
||||
|
||||
const uniq = (a: Dayjs[]) => {
|
||||
const seen: Record<string, boolean> = {};
|
||||
return a.filter((item) => {
|
||||
return seen.hasOwnProperty(item.format()) ? false : (seen[item.format()] = true);
|
||||
});
|
||||
};
|
||||
|
||||
return uniq(slots);
|
||||
};
|
||||
|
||||
export default getSlots;
|
||||
/** Prefer import from `@calcom/lib/slots` */
|
||||
export * from "@calcom/lib/slots";
|
||||
export { default } from "@calcom/lib/slots";
|
||||
|
|
|
@ -1,8 +1,17 @@
|
|||
import { WebhookTriggerEvents } from "@prisma/client";
|
||||
|
||||
// this is exported as we can't use `WebhookTriggerEvents` in the frontend straight-off
|
||||
|
||||
export const WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP = {
|
||||
core: [
|
||||
WebhookTriggerEvents.BOOKING_CANCELLED,
|
||||
WebhookTriggerEvents.BOOKING_CREATED,
|
||||
WebhookTriggerEvents.BOOKING_RESCHEDULED,
|
||||
] as ["BOOKING_CANCELLED", "BOOKING_CREATED", "BOOKING_RESCHEDULED"],
|
||||
routing_forms: [WebhookTriggerEvents.FORM_SUBMITTED] as ["FORM_SUBMITTED"],
|
||||
};
|
||||
|
||||
export const WEBHOOK_TRIGGER_EVENTS = [
|
||||
WebhookTriggerEvents.BOOKING_CANCELLED,
|
||||
WebhookTriggerEvents.BOOKING_CREATED,
|
||||
WebhookTriggerEvents.BOOKING_RESCHEDULED,
|
||||
] as ["BOOKING_CANCELLED", "BOOKING_CREATED", "BOOKING_RESCHEDULED"];
|
||||
...WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP.core,
|
||||
...WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP.routing_forms,
|
||||
] as ["BOOKING_CANCELLED", "BOOKING_CREATED", "BOOKING_RESCHEDULED", "FORM_SUBMITTED"];
|
||||
|
|
|
@ -34,10 +34,7 @@ const sendPayload = async (
|
|||
bookingId?: number;
|
||||
}
|
||||
) => {
|
||||
const { subscriberUrl, appId, payloadTemplate: template } = webhook;
|
||||
if (!subscriberUrl || !data) {
|
||||
throw new Error("Missing required elements to send webhook payload.");
|
||||
}
|
||||
const { appId, payloadTemplate: template } = webhook;
|
||||
|
||||
const contentType =
|
||||
!template || jsonParse(template) ? "application/json" : "application/x-www-form-urlencoded";
|
||||
|
@ -59,6 +56,33 @@ const sendPayload = async (
|
|||
});
|
||||
}
|
||||
|
||||
return _sendPayload(secretKey, triggerEvent, createdAt, webhook, body, contentType);
|
||||
};
|
||||
|
||||
export const sendGenericWebhookPayload = async (
|
||||
secretKey: string | null,
|
||||
triggerEvent: string,
|
||||
createdAt: string,
|
||||
webhook: Pick<Webhook, "subscriberUrl" | "appId" | "payloadTemplate">,
|
||||
data: Record<string, unknown>
|
||||
) => {
|
||||
const body = JSON.stringify(data);
|
||||
return _sendPayload(secretKey, triggerEvent, createdAt, webhook, body, "application/json");
|
||||
};
|
||||
|
||||
const _sendPayload = async (
|
||||
secretKey: string | null,
|
||||
triggerEvent: string,
|
||||
createdAt: string,
|
||||
webhook: Pick<Webhook, "subscriberUrl" | "appId" | "payloadTemplate">,
|
||||
body: string,
|
||||
contentType: "application/json" | "application/x-www-form-urlencoded"
|
||||
) => {
|
||||
const { subscriberUrl } = webhook;
|
||||
if (!subscriberUrl || !body) {
|
||||
throw new Error("Missing required elements to send webhook payload.");
|
||||
}
|
||||
|
||||
const secretSignature = secretKey
|
||||
? createHmac("sha256", secretKey).update(`${body}`).digest("hex")
|
||||
: "no-secret-provided";
|
||||
|
|
|
@ -1,36 +1,10 @@
|
|||
const path = require("path");
|
||||
const i18nConfig = require("@calcom/config/next-i18next.config");
|
||||
|
||||
/** @type {import("next-i18next").UserConfig} */
|
||||
const config = {
|
||||
i18n: {
|
||||
defaultLocale: "en",
|
||||
locales: [
|
||||
"en",
|
||||
"fr",
|
||||
"it",
|
||||
"ru",
|
||||
"es",
|
||||
"de",
|
||||
"pt",
|
||||
"ro",
|
||||
"nl",
|
||||
"pt-BR",
|
||||
"es-419",
|
||||
"ko",
|
||||
"ja",
|
||||
"pl",
|
||||
"ar",
|
||||
"iw",
|
||||
"zh-CN",
|
||||
"zh-TW",
|
||||
"cs",
|
||||
"sr",
|
||||
"sv",
|
||||
"vi",
|
||||
],
|
||||
},
|
||||
...i18nConfig,
|
||||
localePath: path.resolve("./public/static/locales"),
|
||||
reloadOnPrerender: process.env.NODE_ENV !== "production",
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
|
|
|
@ -5,14 +5,15 @@ const withTM = require("next-transpile-modules")([
|
|||
"@calcom/core",
|
||||
"@calcom/dayjs",
|
||||
"@calcom/ee",
|
||||
"@calcom/lib",
|
||||
"@calcom/prisma",
|
||||
"@calcom/stripe",
|
||||
"@calcom/ui",
|
||||
"@calcom/emails",
|
||||
"@calcom/embed-core",
|
||||
"@calcom/embed-react",
|
||||
"@calcom/embed-snippet",
|
||||
"@calcom/lib",
|
||||
"@calcom/prisma",
|
||||
"@calcom/stripe",
|
||||
"@calcom/trpc",
|
||||
"@calcom/ui",
|
||||
]);
|
||||
const { i18n } = require("./next-i18next.config");
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next",
|
||||
"dev": "next dev",
|
||||
"dx": "yarn dev",
|
||||
"test": "jest",
|
||||
"test": "dotenv -e ./test/.env.test -- jest",
|
||||
"db-setup-tests": "dotenv -e ./test/.env.test -- yarn workspace @calcom/prisma prisma migrate deploy",
|
||||
"test-e2e": "cd ../.. && yarn playwright test --config=tests/config/playwright.config.ts --project=chromium",
|
||||
"playwright-report": "playwright show-report playwright/reports/playwright-html-report",
|
||||
"test-codegen": "yarn playwright codegen http://localhost:3000",
|
||||
|
@ -27,8 +28,8 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@boxyhq/saml-jackson": "0.3.6",
|
||||
"@calcom/app-store": "*",
|
||||
"@calcom/app-store-cli": "*",
|
||||
"@calcom/app-store": "*",
|
||||
"@calcom/core": "*",
|
||||
"@calcom/dayjs": "*",
|
||||
"@calcom/ee": "*",
|
||||
|
@ -38,6 +39,7 @@
|
|||
"@calcom/lib": "*",
|
||||
"@calcom/prisma": "*",
|
||||
"@calcom/stripe": "*",
|
||||
"@calcom/trpc": "*",
|
||||
"@calcom/tsconfig": "*",
|
||||
"@calcom/ui": "*",
|
||||
"@daily-co/daily-js": "^0.26.0",
|
||||
|
@ -60,10 +62,6 @@
|
|||
"@radix-ui/react-tooltip": "^0.1.0",
|
||||
"@stripe/react-stripe-js": "^1.8.0",
|
||||
"@stripe/stripe-js": "^1.29.0",
|
||||
"@trpc/client": "^9.25.2",
|
||||
"@trpc/next": "^9.25.2",
|
||||
"@trpc/react": "^9.25.2",
|
||||
"@trpc/server": "^9.25.2",
|
||||
"@vercel/edge-functions-ui": "^0.2.1",
|
||||
"@wojtekmaj/react-daterange-picker": "^3.3.1",
|
||||
"accept-language-parser": "^1.5.0",
|
||||
|
@ -89,6 +87,7 @@
|
|||
"next-mdx-remote": "^4.0.3",
|
||||
"next-seo": "^4.26.0",
|
||||
"next-transpile-modules": "^9.0.0",
|
||||
"nock": "^13.2.8",
|
||||
"nodemailer": "^6.7.5",
|
||||
"otplib": "^12.0.1",
|
||||
"qrcode": "^1.5.0",
|
||||
|
|
|
@ -4,6 +4,14 @@ import Head from "next/head";
|
|||
import superjson from "superjson";
|
||||
|
||||
import "@calcom/embed-core/src/embed-iframe";
|
||||
import { httpBatchLink } from "@calcom/trpc/client/links/httpBatchLink";
|
||||
import { httpLink } from "@calcom/trpc/client/links/httpLink";
|
||||
import { loggerLink } from "@calcom/trpc/client/links/loggerLink";
|
||||
import { splitLink } from "@calcom/trpc/client/links/splitLink";
|
||||
import { withTRPC } from "@calcom/trpc/next";
|
||||
import type { TRPCClientErrorLike } from "@calcom/trpc/react";
|
||||
import { Maybe } from "@calcom/trpc/server";
|
||||
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||
import LicenseRequired from "@ee/components/LicenseRequired";
|
||||
|
||||
import AppProviders, { AppProps } from "@lib/app-providers";
|
||||
|
@ -12,15 +20,6 @@ import useTheme from "@lib/hooks/useTheme";
|
|||
|
||||
import I18nLanguageHandler from "@components/I18nLanguageHandler";
|
||||
|
||||
import type { AppRouter } from "@server/routers/_app";
|
||||
import { httpBatchLink } from "@trpc/client/links/httpBatchLink";
|
||||
import { httpLink } from "@trpc/client/links/httpLink";
|
||||
import { loggerLink } from "@trpc/client/links/loggerLink";
|
||||
import { splitLink } from "@trpc/client/links/splitLink";
|
||||
import { withTRPC } from "@trpc/next";
|
||||
import type { TRPCClientErrorLike } from "@trpc/react";
|
||||
import { Maybe } from "@trpc/server";
|
||||
|
||||
import { ContractsProvider } from "../contexts/contractsContext";
|
||||
import "../styles/fonts.css";
|
||||
import "../styles/globals.css";
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import { getSession } from "@lib/auth";
|
||||
import { createContext } from "@calcom/trpc/server/createContext";
|
||||
import { viewerRouter } from "@calcom/trpc/server/routers/viewer";
|
||||
|
||||
import { createContext } from "@server/createContext";
|
||||
import { viewerRouter } from "@server/routers/viewer";
|
||||
import { getSession } from "@lib/auth";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const session = await getSession({ req });
|
||||
|
|
|
@ -8,8 +8,8 @@ import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib";
|
|||
import logger from "@calcom/lib/logger";
|
||||
import { defaultHandler, defaultResponder } from "@calcom/lib/server";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { refund } from "@calcom/stripe/server";
|
||||
import type { AdditionalInformation, CalendarEvent } from "@calcom/types/Calendar";
|
||||
import { refund } from "@ee/lib/stripe/server";
|
||||
import { scheduleWorkflowReminders } from "@ee/lib/workflows/reminders/reminderScheduler";
|
||||
|
||||
import { getSession } from "@lib/auth";
|
||||
|
|
|
@ -18,20 +18,20 @@ import {
|
|||
import { getLuckyUsers, isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib";
|
||||
import { getDefaultEvent, getGroupName, getUsernameList } from "@calcom/lib/defaultEvents";
|
||||
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||
import isOutOfBounds from "@calcom/lib/isOutOfBounds";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import { defaultResponder } from "@calcom/lib/server";
|
||||
import prisma, { userSelect } from "@calcom/prisma";
|
||||
import { extendedBookingCreateBody } from "@calcom/prisma/zod-utils";
|
||||
import { handlePayment } from "@calcom/stripe/server";
|
||||
import type { BufferedBusyTime } from "@calcom/types/BufferedBusyTime";
|
||||
import type { AdditionalInformation, CalendarEvent } from "@calcom/types/Calendar";
|
||||
import type { EventResult, PartialReference } from "@calcom/types/EventManager";
|
||||
import { handlePayment } from "@ee/lib/stripe/server";
|
||||
import { scheduleWorkflowReminders } from "@ee/lib/workflows/reminders/reminderScheduler";
|
||||
|
||||
import { HttpError } from "@lib/core/http/error";
|
||||
import { ensureArray } from "@lib/ensureArray";
|
||||
import { getEventName } from "@lib/event";
|
||||
import isOutOfBounds from "@lib/isOutOfBounds";
|
||||
import sendPayload from "@lib/webhooks/sendPayload";
|
||||
import getSubscribers from "@lib/webhooks/subscriptions";
|
||||
|
||||
|
|
|
@ -19,8 +19,8 @@ import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib";
|
|||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import { defaultHandler, defaultResponder } from "@calcom/lib/server";
|
||||
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
||||
import { refund } from "@calcom/stripe/server";
|
||||
import type { CalendarEvent } from "@calcom/types/Calendar";
|
||||
import { refund } from "@ee/lib/stripe/server";
|
||||
import { deleteScheduledEmailReminder } from "@ee/lib/workflows/reminders/emailReminderManager";
|
||||
import { sendCancelledReminders } from "@ee/lib/workflows/reminders/reminderScheduler";
|
||||
import { deleteScheduledSMSReminder } from "@ee/lib/workflows/reminders/smsReminderManager";
|
||||
|
|
|
@ -2,11 +2,10 @@ import type { NextApiRequest, NextApiResponse } from "next";
|
|||
import { getSession } from "next-auth/react";
|
||||
|
||||
import { defaultHandler } from "@calcom/lib/server";
|
||||
import { checkUsername } from "@calcom/lib/server/checkUsername";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { userMetadata as zodUserMetadata } from "@calcom/prisma/zod-utils";
|
||||
|
||||
import { checkUsername } from "@lib/core/server/checkUsername";
|
||||
|
||||
export async function getHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { intentUsername } = req.body;
|
||||
// Check that user is authenticated
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import { getTeamWithMembers } from "@calcom/lib/server/queries/teams";
|
||||
|
||||
import { getSession } from "@lib/auth";
|
||||
import prisma from "@lib/prisma";
|
||||
import { getTeamWithMembers } from "@lib/queries/teams";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const session = await getSession({ req: req });
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/**
|
||||
* This file contains tRPC's HTTP response handler
|
||||
*/
|
||||
import { createContext } from "@server/createContext";
|
||||
import { appRouter } from "@server/routers/_app";
|
||||
import * as trpcNext from "@trpc/server/adapters/next";
|
||||
import * as trpcNext from "@calcom/trpc/server/adapters/next";
|
||||
import { createContext } from "@calcom/trpc/server/createContext";
|
||||
import { appRouter } from "@calcom/trpc/server/routers/_app";
|
||||
|
||||
export default trpcNext.createNextApiHandler({
|
||||
router: appRouter,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import { checkUsername } from "@lib/core/server/checkUsername";
|
||||
import { checkUsername } from "@calcom/lib/server/checkUsername";
|
||||
|
||||
type Response = {
|
||||
available: boolean;
|
||||
|
|
|
@ -6,6 +6,7 @@ import { InstallAppButton } from "@calcom/app-store/components";
|
|||
import { WEBSITE_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import type { App } from "@calcom/types/App";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import Button from "@calcom/ui/Button";
|
||||
|
@ -14,7 +15,6 @@ import EmptyScreen from "@calcom/ui/EmptyScreen";
|
|||
import { QueryCell } from "@lib/QueryCell";
|
||||
import classNames from "@lib/classNames";
|
||||
import { HttpError } from "@lib/core/http/error";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import AppsShell from "@components/AppsShell";
|
||||
import { List, ListItem, ListItemText, ListItemTitle } from "@components/List";
|
||||
|
|
|
@ -3,12 +3,12 @@ import { signIn } from "next-auth/react";
|
|||
import { useRouter } from "next/router";
|
||||
import { useEffect } from "react";
|
||||
|
||||
import { checkUsername } from "@calcom/lib/server/checkUsername";
|
||||
import stripe from "@calcom/stripe/server";
|
||||
import { getPremiumPlanPrice } from "@calcom/stripe/utils";
|
||||
|
||||
import { asStringOrNull } from "@lib/asStringOrNull";
|
||||
import { getSession } from "@lib/auth";
|
||||
import { checkUsername } from "@lib/core/server/checkUsername";
|
||||
import prisma from "@lib/prisma";
|
||||
import { hostedCal, isSAMLLoginEnabled, samlProductID, samlTenantID, samlTenantProduct } from "@lib/saml";
|
||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
|
|
|
@ -6,13 +6,13 @@ import { Controller, useForm } from "react-hook-form";
|
|||
import { DEFAULT_SCHEDULE, availabilityAsString } from "@calcom/lib/availability";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import Switch from "@calcom/ui/Switch";
|
||||
import { Form } from "@calcom/ui/form/fields";
|
||||
|
||||
import { QueryCell } from "@lib/QueryCell";
|
||||
import { HttpError } from "@lib/core/http/error";
|
||||
import { inferQueryOutput, trpc } from "@lib/trpc";
|
||||
|
||||
import Shell from "@components/Shell";
|
||||
import Schedule from "@components/availability/Schedule";
|
||||
|
@ -141,7 +141,7 @@ export default function Availability() {
|
|||
success={({ data }) => {
|
||||
return (
|
||||
<Shell
|
||||
heading={<EditableHeading title={data.schedule.name} onChange={setName} />}
|
||||
heading={<EditableHeading title={name || data.schedule.name} onChange={setName} />}
|
||||
subtitle={data.schedule.availability.map((availability) => (
|
||||
<span key={availability.id}>
|
||||
{availabilityAsString(availability, i18n.language)}
|
||||
|
|
|
@ -2,11 +2,11 @@ import { ClockIcon } from "@heroicons/react/outline";
|
|||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import EmptyScreen from "@calcom/ui/EmptyScreen";
|
||||
|
||||
import { withQuery } from "@lib/QueryCell";
|
||||
import { HttpError } from "@lib/core/http/error";
|
||||
import { inferQueryOutput, trpc } from "@lib/trpc";
|
||||
|
||||
import Shell from "@components/Shell";
|
||||
import { NewScheduleButton } from "@components/availability/NewScheduleButton";
|
||||
|
|
|
@ -2,9 +2,9 @@ import { useEffect, useState } from "react";
|
|||
|
||||
import dayjs, { Dayjs } from "@calcom/dayjs";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
|
||||
import { QueryCell } from "@lib/QueryCell";
|
||||
import { inferQueryOutput, trpc } from "@lib/trpc";
|
||||
|
||||
import Loader from "@components/Loader";
|
||||
import Shell from "@components/Shell";
|
||||
|
|
|
@ -4,12 +4,12 @@ import { Fragment } from "react";
|
|||
|
||||
import { WipeMyCalActionButton } from "@calcom/app-store/wipemycalother/components";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { inferQueryInput, inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import EmptyScreen from "@calcom/ui/EmptyScreen";
|
||||
|
||||
import { useInViewObserver } from "@lib/hooks/useInViewObserver";
|
||||
import { inferQueryInput, inferQueryOutput, trpc } from "@lib/trpc";
|
||||
|
||||
import BookingsShell from "@components/BookingsShell";
|
||||
import Shell from "@components/Shell";
|
||||
|
@ -76,7 +76,7 @@ export default function Bookings() {
|
|||
};
|
||||
return (
|
||||
<Shell heading={t("bookings")} subtitle={t("bookings_description")} customLoader={<SkeletonLoader />}>
|
||||
<WipeMyCalActionButton trpc={trpc} bookingStatus={status} bookingsEmpty={isEmpty} />
|
||||
<WipeMyCalActionButton bookingStatus={status} bookingsEmpty={isEmpty} />
|
||||
<BookingsShell>
|
||||
<div className="-mx-4 flex flex-col sm:mx-auto">
|
||||
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
||||
|
|
|
@ -37,6 +37,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|||
import showToast from "@calcom/lib/notification";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { StripeData } from "@calcom/stripe/server";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { RecurringEvent } from "@calcom/types/Calendar";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import Button from "@calcom/ui/Button";
|
||||
|
@ -52,7 +53,6 @@ import { HttpError } from "@lib/core/http/error";
|
|||
import { isSuccessRedirectAvailable } from "@lib/isSuccessRedirectAvailable";
|
||||
import { LocationObject, LocationType } from "@lib/location";
|
||||
import { slugify } from "@lib/slugify";
|
||||
import { trpc } from "@lib/trpc";
|
||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
|
||||
import { ClientSuspense } from "@components/ClientSuspense";
|
||||
|
@ -67,6 +67,7 @@ import { EditLocationDialog } from "@components/dialog/EditLocationDialog";
|
|||
import RecurringEventController from "@components/eventtype/RecurringEventController";
|
||||
import CustomInputTypeForm from "@components/pages/eventtypes/CustomInputTypeForm";
|
||||
import Badge from "@components/ui/Badge";
|
||||
import EditableHeading from "@components/ui/EditableHeading";
|
||||
import InfoBadge from "@components/ui/InfoBadge";
|
||||
import CheckboxField from "@components/ui/form/CheckboxField";
|
||||
import CheckedSelect from "@components/ui/form/CheckedSelect";
|
||||
|
@ -77,6 +78,7 @@ import * as RadioArea from "@components/ui/form/radio-area";
|
|||
import WebhookListContainer from "@components/webhook/WebhookListContainer";
|
||||
|
||||
import { getTranslation } from "@server/lib/i18n";
|
||||
import { TRPCClientError } from "@trpc/client";
|
||||
|
||||
interface Token {
|
||||
name?: string;
|
||||
|
@ -302,12 +304,13 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
if (err instanceof HttpError) {
|
||||
const message = `${err.statusCode}: ${err.message}`;
|
||||
showToast(message, "error");
|
||||
} else if (err instanceof TRPCClientError) {
|
||||
showToast(err.message, "error");
|
||||
}
|
||||
},
|
||||
});
|
||||
const connectedCalendarsQuery = trpc.useQuery(["viewer.connectedCalendars"]);
|
||||
|
||||
const [editIcon, setEditIcon] = useState(true);
|
||||
const [showLocationModal, setShowLocationModal] = useState(false);
|
||||
const [selectedLocation, setSelectedLocation] = useState<OptionTypeBase | undefined>(undefined);
|
||||
const [selectedCustomInput, setSelectedCustomInput] = useState<EventTypeCustomInput | undefined>(undefined);
|
||||
|
@ -315,7 +318,6 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
const [customInputs, setCustomInputs] = useState<EventTypeCustomInput[]>(
|
||||
eventType.customInputs.sort((a, b) => a.id - b.id) || []
|
||||
);
|
||||
const [tokensList, setTokensList] = useState<Array<Token>>([]);
|
||||
|
||||
const defaultSeatsPro = 6;
|
||||
const minSeats = 2;
|
||||
|
@ -351,7 +353,6 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
|
||||
async function deleteEventTypeHandler(event: React.MouseEvent<HTMLElement, MouseEvent>) {
|
||||
event.preventDefault();
|
||||
|
||||
const payload = { id: eventType.id };
|
||||
deleteMutation.mutate(payload);
|
||||
}
|
||||
|
@ -437,6 +438,7 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
|
||||
const formMethods = useForm<FormValues>({
|
||||
defaultValues: {
|
||||
title: eventType.title,
|
||||
locations: eventType.locations || [],
|
||||
recurringEvent: eventType.recurringEvent || null,
|
||||
schedule: eventType.schedule?.id,
|
||||
|
@ -844,37 +846,10 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
<Shell
|
||||
title={t("event_type_title", { eventTypeTitle: eventType.title })}
|
||||
heading={
|
||||
<div className="group relative cursor-pointer" onClick={() => setEditIcon(false)}>
|
||||
{editIcon ? (
|
||||
<>
|
||||
<h1
|
||||
style={{ fontSize: 22, letterSpacing: "-0.0009em" }}
|
||||
className="inline pl-0 text-gray-900 focus:text-black group-hover:text-gray-500">
|
||||
{formMethods.getValues("title") && formMethods.getValues("title") !== ""
|
||||
? formMethods.getValues("title")
|
||||
: eventType.title}
|
||||
</h1>
|
||||
<PencilIcon className="ml-1 -mt-1 inline h-4 w-4 text-gray-700 group-hover:text-gray-500" />
|
||||
</>
|
||||
) : (
|
||||
<div style={{ marginBottom: -11 }}>
|
||||
<input
|
||||
type="text"
|
||||
autoFocus
|
||||
style={{ top: -6, fontSize: 22 }}
|
||||
required
|
||||
className="relative h-10 w-full cursor-pointer border-none bg-transparent pl-0 text-gray-900 hover:text-gray-700 focus:text-black focus:outline-none focus:ring-0"
|
||||
placeholder={t("quick_chat")}
|
||||
{...formMethods.register("title")}
|
||||
defaultValue={eventType.title}
|
||||
onBlur={() => {
|
||||
setEditIcon(true);
|
||||
formMethods.getValues("title") === "" && formMethods.setValue("title", eventType.title);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<EditableHeading
|
||||
title={formMethods.watch("title")}
|
||||
onChange={(value) => formMethods.setValue("title", value)}
|
||||
/>
|
||||
}
|
||||
subtitle={eventType.description || ""}>
|
||||
<ClientSuspense fallback={<Loader />}>
|
||||
|
@ -1978,20 +1953,24 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
className="text-md flex items-center rounded-sm px-2 py-1 text-sm font-medium text-gray-700 hover:bg-gray-200 hover:text-gray-900"
|
||||
eventTypeId={eventType.id}
|
||||
/>
|
||||
<Dialog>
|
||||
<DialogTrigger className="text-md flex items-center rounded-sm px-2 py-1 text-sm font-medium text-red-500 hover:bg-gray-200">
|
||||
<TrashIcon className="h-4 w-4 text-red-500 ltr:mr-2 rtl:ml-2" />
|
||||
{t("delete")}
|
||||
</DialogTrigger>
|
||||
<ConfirmationDialogContent
|
||||
isLoading={deleteMutation.isLoading}
|
||||
variety="danger"
|
||||
title={t("delete_event_type")}
|
||||
confirmBtnText={t("confirm_delete_event_type")}
|
||||
onConfirm={deleteEventTypeHandler}>
|
||||
{t("delete_event_type_description")}
|
||||
</ConfirmationDialogContent>
|
||||
</Dialog>
|
||||
{/* This will only show if the user is not a member (ADMIN,OWNER) and if there is no current membership
|
||||
- meaning you are within an eventtype that does not belong to a team */}
|
||||
{(props.currentUserMembership?.role !== "MEMBER" || !props.currentUserMembership) && (
|
||||
<Dialog>
|
||||
<DialogTrigger className="text-md flex items-center rounded-sm px-2 py-1 text-sm font-medium text-red-500 hover:bg-gray-200">
|
||||
<TrashIcon className="h-4 w-4 text-red-500 ltr:mr-2 rtl:ml-2" />
|
||||
{t("delete")}
|
||||
</DialogTrigger>
|
||||
<ConfirmationDialogContent
|
||||
isLoading={deleteMutation.isLoading}
|
||||
variety="danger"
|
||||
title={t("delete_event_type")}
|
||||
confirmBtnText={t("confirm_delete_event_type")}
|
||||
onConfirm={deleteEventTypeHandler}>
|
||||
{t("delete_event_type_description")}
|
||||
</ConfirmationDialogContent>
|
||||
</Dialog>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2281,6 +2260,11 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
})
|
||||
: [];
|
||||
|
||||
// Find the current users memebership so we can check role to enable/disable deletion.
|
||||
// Sets to null if no membership is found - this must mean we are in a none team event type
|
||||
const currentUserMembership =
|
||||
eventTypeObject.team?.members.find((el) => el.user.id === session.user.id) ?? null;
|
||||
|
||||
return {
|
||||
props: {
|
||||
session,
|
||||
|
@ -2292,6 +2276,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
hasPaymentIntegration,
|
||||
hasGiphyIntegration,
|
||||
currency,
|
||||
currentUserMembership,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -22,6 +22,7 @@ import React, { Fragment, useEffect, useState } from "react";
|
|||
import { CAL_URL, WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import { Button } from "@calcom/ui";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import { Dialog } from "@calcom/ui/Dialog";
|
||||
|
@ -37,7 +38,6 @@ import { Tooltip } from "@calcom/ui/Tooltip";
|
|||
import { withQuery } from "@lib/QueryCell";
|
||||
import classNames from "@lib/classNames";
|
||||
import { HttpError } from "@lib/core/http/error";
|
||||
import { inferQueryOutput, trpc } from "@lib/trpc";
|
||||
|
||||
import { EmbedButton, EmbedDialog } from "@components/Embed";
|
||||
import Shell from "@components/Shell";
|
||||
|
@ -49,6 +49,8 @@ import Avatar from "@components/ui/Avatar";
|
|||
import AvatarGroup from "@components/ui/AvatarGroup";
|
||||
import Badge from "@components/ui/Badge";
|
||||
|
||||
import { TRPCClientError } from "@trpc/react";
|
||||
|
||||
type EventTypeGroups = inferQueryOutput<"viewer.eventTypes">["eventTypeGroups"];
|
||||
type EventTypeGroupProfile = EventTypeGroups[number]["profile"];
|
||||
interface EventTypeListHeadingProps {
|
||||
|
@ -193,6 +195,8 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
|||
const message = `${err.statusCode}: ${err.message}`;
|
||||
showToast(message, "error");
|
||||
setDeleteDialogOpen(false);
|
||||
} else if (err instanceof TRPCClientError) {
|
||||
showToast(err.message, "error");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -332,19 +336,22 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
|||
/>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator className="h-px bg-gray-200" />
|
||||
<DropdownMenuItem>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setDeleteDialogOpen(true);
|
||||
setDeleteDialogTypeId(type.id);
|
||||
}}
|
||||
color="warn"
|
||||
size="sm"
|
||||
StartIcon={TrashIcon}
|
||||
className="w-full rounded-none">
|
||||
{t("delete") as string}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
{/* readonly is only set when we are on a team - if we are on a user event type null will be the value. */}
|
||||
{(group.metadata?.readOnly === false || group.metadata.readOnly === null) && (
|
||||
<DropdownMenuItem>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setDeleteDialogOpen(true);
|
||||
setDeleteDialogTypeId(type.id);
|
||||
}}
|
||||
color="warn"
|
||||
size="sm"
|
||||
StartIcon={TrashIcon}
|
||||
className="w-full rounded-none">
|
||||
{t("delete") as string}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
|
|
@ -17,6 +17,7 @@ import { getCalendarCredentials, getConnectedCalendars } from "@calcom/core/Cale
|
|||
import dayjs from "@calcom/dayjs";
|
||||
import { DOCS_URL } from "@calcom/lib/constants";
|
||||
import { fetchUsername } from "@calcom/lib/fetchUsername";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Form } from "@calcom/ui/form/fields";
|
||||
|
@ -26,7 +27,6 @@ import { DEFAULT_SCHEDULE } from "@lib/availability";
|
|||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import prisma from "@lib/prisma";
|
||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry";
|
||||
import { trpc } from "@lib/trpc";
|
||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
import { Schedule as ScheduleType } from "@lib/types/schedule";
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import ApiKeyListContainer from "@ee/components/apiKeys/ApiKeyListContainer";
|
||||
|
||||
import SettingsShell from "@components/SettingsShell";
|
||||
|
@ -6,10 +7,23 @@ import WebhookListContainer from "@components/webhook/WebhookListContainer";
|
|||
|
||||
export default function Settings() {
|
||||
const { t } = useLocale();
|
||||
const { data: routingForms } = trpc.useQuery([
|
||||
"viewer.appById",
|
||||
{
|
||||
appId: "routing_forms",
|
||||
},
|
||||
]);
|
||||
|
||||
return (
|
||||
<SettingsShell heading={t("developer")} subtitle={t("manage_developer_settings")}>
|
||||
<WebhookListContainer title={t("webhooks")} subtitle={t("receive_cal_meeting_data")} />
|
||||
<WebhookListContainer title="Event Webhooks" subtitle={t("receive_cal_meeting_data")} />
|
||||
{routingForms && (
|
||||
<WebhookListContainer
|
||||
appId="routing_forms"
|
||||
title="Routing Webhooks"
|
||||
subtitle="Receive Routing Form responses at a specified URL, in real-time, when a Routing Form is submitted"
|
||||
/>
|
||||
)}
|
||||
<ApiKeyListContainer />
|
||||
</SettingsShell>
|
||||
);
|
||||
|
|
|
@ -8,6 +8,9 @@ import TimezoneSelect, { ITimezone } from "react-timezone-select";
|
|||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { TRPCClientErrorLike } from "@calcom/trpc/client";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogTrigger } from "@calcom/ui/Dialog";
|
||||
|
@ -18,7 +21,6 @@ import { getSession } from "@lib/auth";
|
|||
import { nameOfDay } from "@lib/core/i18n/weekday";
|
||||
import { isBrandingHidden } from "@lib/isBrandingHidden";
|
||||
import prisma from "@lib/prisma";
|
||||
import { trpc } from "@lib/trpc";
|
||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
|
||||
import ImageUploader from "@components/ImageUploader";
|
||||
|
@ -31,9 +33,6 @@ import { UsernameAvailability } from "@components/ui/UsernameAvailability";
|
|||
import ColorPicker from "@components/ui/colorpicker";
|
||||
import Select from "@components/ui/form/Select";
|
||||
|
||||
import { AppRouter } from "@server/routers/_app";
|
||||
import { TRPCClientErrorLike } from "@trpc/client";
|
||||
|
||||
import { UpgradeToProDialog } from "../../components/UpgradeToProDialog";
|
||||
|
||||
type Props = inferSSRProps<typeof getServerSideProps>;
|
||||
|
|
|
@ -2,10 +2,10 @@ import { IdentityProvider } from "@prisma/client";
|
|||
import React from "react";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import SAMLConfiguration from "@ee/components/saml/Configuration";
|
||||
|
||||
import { identityProviderNameMap } from "@lib/auth";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import SettingsShell from "@components/SettingsShell";
|
||||
import ChangePasswordSection from "@components/security/ChangePasswordSection";
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user