chore: bump @trpc/* and @tanstack/react-query (#13335)

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Benny Joo <sldisek783@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: Gergő Móricz <mo.geryy@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
This commit is contained in:
Alex / KATT
2024-01-23 16:42:29 -07:00
committed by GitHub
co-authored by Peer Richelsen Omar López CarinaWolli Alex van Andel Udit Takkar Benny Joo Carina Wollendorfer Gergő Móricz Keith Williams
parent 12eec186b2
commit 30ba168fc3
183 changed files with 1203 additions and 1013 deletions
+9 -4
View File
@@ -1,11 +1,16 @@
"use client";
import { type DehydratedState, Hydrate } from "@tanstack/react-query";
import { type DehydratedState, HydrationBoundary } from "@tanstack/react-query";
import { useMemo } from "react";
import type { DataTransformer } from "@trpc/server";
type DataTransformer = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
serialize: (data: any) => any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
deserialize: (data: any) => any;
};
export function createHydrateClient(opts: { transformer?: DataTransformer }) {
export function createHydrateClient(opts: { transformer: DataTransformer }) {
return function HydrateClient(props: { children: React.ReactNode; state: DehydratedState }) {
const { state, children } = props;
@@ -16,6 +21,6 @@ export function createHydrateClient(opts: { transformer?: DataTransformer }) {
return state;
}, [state]);
return <Hydrate state={transformedState}>{children}</Hydrate>;
return <HydrationBoundary state={transformedState}>{children}</HydrationBoundary>;
};
}
+1 -4
View File
@@ -4,10 +4,7 @@ import { trpc } from "app/_trpc/client";
import { useState } from "react";
import superjson from "superjson";
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 { httpBatchLink, httpLink, loggerLink, splitLink } from "@calcom/trpc/client";
import { ENDPOINTS } from "@calcom/trpc/react/shared";
export type Endpoint = (typeof ENDPOINTS)[number];
@@ -16,10 +16,10 @@ import { Plus } from "@calcom/ui/components/icon";
import { QueryCell } from "@lib/QueryCell";
interface AdditionalCalendarSelectorProps {
isLoading?: boolean;
isPending?: boolean;
}
const AdditionalCalendarSelector = ({ isLoading }: AdditionalCalendarSelectorProps): JSX.Element | null => {
const AdditionalCalendarSelector = ({ isPending }: AdditionalCalendarSelectorProps): JSX.Element | null => {
const { t } = useLocale();
const query = trpc.viewer.integrations.useQuery({ variant: "calendar", onlyInstalled: true });
@@ -42,7 +42,7 @@ const AdditionalCalendarSelector = ({ isLoading }: AdditionalCalendarSelectorPro
return (
<Dropdown modal={false}>
<DropdownMenuTrigger asChild>
<Button StartIcon={Plus} color="secondary" {...(isLoading && { loading: isLoading })}>
<Button StartIcon={Plus} color="secondary" {...(isPending && { loading: isPending })}>
{t("add")}
</Button>
</DropdownMenuTrigger>
+18 -16
View File
@@ -1,6 +1,6 @@
import Link from "next/link";
import type { IframeHTMLAttributes } from "react";
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import useAddAppMutation from "@calcom/app-store/_utils/useAddAppMutation";
import { AppDependencyComponent, InstallAppButton } from "@calcom/app-store/components";
@@ -92,17 +92,19 @@ export const AppPage = ({
const [existingCredentials, setExistingCredentials] = useState<number[]>([]);
const [showDisconnectIntegration, setShowDisconnectIntegration] = useState(false);
const appDbQuery = trpc.viewer.appCredentialsByType.useQuery(
{ appType: type },
{
onSettled(data) {
const credentialsCount = data?.credentials.length || 0;
setShowDisconnectIntegration(
data?.userAdminTeams.length ? credentialsCount >= data?.userAdminTeams.length : credentialsCount > 0
);
setExistingCredentials(data?.credentials.map((credential) => credential.id) || []);
},
}
const appDbQuery = trpc.viewer.appCredentialsByType.useQuery({ appType: type });
useEffect(
function refactorMeWithoutEffect() {
const data = appDbQuery.data;
const credentialsCount = data?.credentials.length || 0;
setShowDisconnectIntegration(
data?.userAdminTeams.length ? credentialsCount >= data?.userAdminTeams.length : credentialsCount > 0
);
setExistingCredentials(data?.credentials.map((credential) => credential.id) || []);
},
[appDbQuery.data]
);
const dependencyData = trpc.viewer.appsRouter.queryForDependencies.useQuery(dependencies, {
@@ -190,7 +192,7 @@ export const AppPage = ({
)}
</header>
</div>
{!appDbQuery.isLoading ? (
{!appDbQuery.isPending ? (
isGlobal ||
(existingCredentials.length > 0 && allowedMultipleInstalls ? (
<div className="flex space-x-3">
@@ -211,7 +213,7 @@ export const AppPage = ({
onClick: () => {
mutation.mutate({ type, variant, slug });
},
loading: mutation.isLoading,
loading: mutation.isPending,
};
}
return (
@@ -250,7 +252,7 @@ export const AppPage = ({
onClick: () => {
mutation.mutate({ type, variant, slug });
},
loading: mutation.isLoading,
loading: mutation.isPending,
};
}
return (
@@ -272,7 +274,7 @@ export const AppPage = ({
)}
{dependencies &&
(!dependencyData.isLoading ? (
(!dependencyData.isPending ? (
<div className="mt-6">
<AppDependencyComponent appName={name} dependencyData={dependencyData.data} />
</div>
@@ -28,7 +28,7 @@ type Props = {
onChanged: () => unknown | Promise<unknown>;
fromOnboarding?: boolean;
destinationCalendarId?: string;
isLoading?: boolean;
isPending?: boolean;
};
function CalendarList(props: Props) {
@@ -74,7 +74,7 @@ function ConnectedCalendarsList(props: Props) {
suspense: true,
refetchOnWindowFocus: false,
});
const { fromOnboarding, isLoading } = props;
const { fromOnboarding, isPending } = props;
return (
<QueryCell
query={query}
@@ -97,7 +97,7 @@ function ConnectedCalendarsList(props: Props) {
<div className="flex flex-col xl:flex-row xl:space-x-5">
{!!data.connectedCalendars.length && (
<div className="flex items-center">
<AdditionalCalendarSelector isLoading={isLoading} />
<AdditionalCalendarSelector isPending={isPending} />
</div>
)}
</div>
@@ -229,7 +229,7 @@ export function CalendarListContainer(props: { heading?: boolean; fromOnboarding
hidePlaceholder
value={data.destinationCalendar?.externalId}
onChange={mutation.mutate}
isLoading={mutation.isLoading}
isPending={mutation.isPending}
/>
</div>
</div>
@@ -239,7 +239,7 @@ export function CalendarListContainer(props: { heading?: boolean; fromOnboarding
onChanged={onChanged}
fromOnboarding={fromOnboarding}
destinationCalendarId={data.destinationCalendar?.externalId}
isLoading={mutation.isLoading}
isPending={mutation.isPending}
/>
</>
)}
@@ -100,9 +100,9 @@ export const InstallAppButtonChild = ({
<DropdownMenuContent
className="w-auto"
onInteractOutside={(event) => {
if (mutation.isLoading) event.preventDefault();
if (mutation.isPending) event.preventDefault();
}}>
{mutation.isLoading && (
{mutation.isPending && (
<div className="z-1 fixed inset-0 flex items-center justify-center">
<Spinner />
</div>
@@ -139,7 +139,7 @@ function BookingListItem(booking: BookingItemProps) {
setRejectionDialogIsOpen(true);
},
icon: Ban,
disabled: mutation.isLoading,
disabled: mutation.isPending,
},
// For bookings with payment, only confirm if the booking is paid for
...((isPending && !paymentAppData.enabled) ||
@@ -153,7 +153,7 @@ function BookingListItem(booking: BookingItemProps) {
bookingConfirm(true);
},
icon: Check,
disabled: mutation.isLoading,
disabled: mutation.isPending,
},
]
: []),
@@ -293,7 +293,7 @@ function BookingListItem(booking: BookingItemProps) {
setViewRecordingsDialogIsOpen(true);
},
color: showCheckRecordingButton ? "secondary" : "primary",
disabled: mutation.isLoading,
disabled: mutation.isPending,
},
];
@@ -348,7 +348,7 @@ function BookingListItem(booking: BookingItemProps) {
<DialogFooter>
<DialogClose />
<Button
disabled={mutation.isLoading}
disabled={mutation.isPending}
data-testid="rejection-confirm"
onClick={() => {
bookingConfirm(false);
@@ -65,7 +65,7 @@ export const ChargeCardDialog = (props: IRescheduleDialog) => {
<DialogClose />
<Button
data-testid="send_request"
disabled={chargeCardMutation.isLoading || chargeError}
disabled={chargeCardMutation.isPending || chargeError}
onClick={() =>
chargeCardMutation.mutate({
bookingId,
@@ -27,7 +27,7 @@ export const RescheduleDialog = (props: IRescheduleDialog) => {
const { isOpenDialog, setIsOpenDialog, bookingUId: bookingId } = props;
const [rescheduleReason, setRescheduleReason] = useState("");
const { mutate: rescheduleApi, isLoading } = trpc.viewer.bookings.requestReschedule.useMutation({
const { mutate: rescheduleApi, isPending } = trpc.viewer.bookings.requestReschedule.useMutation({
async onSuccess() {
showToast(t("reschedule_request_sent"), "success");
setIsOpenDialog(false);
@@ -65,7 +65,7 @@ export const RescheduleDialog = (props: IRescheduleDialog) => {
<DialogClose />
<Button
data-testid="send_request"
disabled={isLoading}
disabled={isPending}
onClick={() => {
rescheduleApi({
bookingId,
@@ -18,7 +18,7 @@ export type EventType = Pick<EventTypeSetupProps, "eventType">["eventType"] &
export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
const { t } = useLocale();
const { data: eventTypeApps, isLoading } = trpc.viewer.integrations.useQuery({
const { data: eventTypeApps, isPending } = trpc.viewer.integrations.useQuery({
extendsFeature: "EventType",
teamId: eventType.team?.id || eventType.parent?.teamId,
});
@@ -138,7 +138,7 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
message={t("locked_apps_description")}
/>
)}
{!isLoading && !installedApps?.length ? (
{!isPending && !installedApps?.length ? (
<EmptyScreen
Icon={Grid}
headline={t("empty_installed_apps_headline")}
@@ -179,7 +179,7 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
</div>
{!shouldLockDisableProps("apps").disabled && (
<div className="bg-muted mt-6 rounded-md p-8">
{!isLoading && notInstalledApps?.length ? (
{!isPending && notInstalledApps?.length ? (
<>
<h2 className="text-emphasis mb-2 text-xl font-semibold leading-5 tracking-[0.01em]">
{t("available_apps_lower_case")}
@@ -85,7 +85,7 @@ const EventTypeScheduleDetails = memo(
const { watch } = useFormContext<FormValues>();
const scheduleId = watch("schedule");
const { isLoading, data: schedule } = trpc.viewer.availability.schedule.get.useQuery(
const { isPending, data: schedule } = trpc.viewer.availability.schedule.get.useQuery(
{
scheduleId:
scheduleId || loggedInUser?.defaultScheduleId || selectedScheduleValue?.value || undefined,
@@ -112,7 +112,7 @@ const EventTypeScheduleDetails = memo(
)}>
{day}
</span>
{isLoading ? (
{isPending ? (
<SkeletonText className="block h-5 w-60" />
) : isAvailable ? (
<div className="space-y-3 text-right">
@@ -142,7 +142,7 @@ const EventTypeScheduleDetails = memo(
{!!schedule?.id && !schedule.isManaged && !schedule.readOnly && (
<Button
href={`/availability/${schedule.id}`}
disabled={isLoading}
disabled={isPending}
color="minimal"
EndIcon={ExternalLink}
target="_blank"
@@ -169,8 +169,15 @@ const EventTypeSchedule = ({ eventType }: { eventType: EventTypeSetup }) => {
const watchSchedule = watch("schedule");
const [options, setOptions] = useState<AvailabilityOption[]>([]);
const { isLoading } = trpc.viewer.availability.list.useQuery(undefined, {
onSuccess: ({ schedules }) => {
const { data, isPending } = trpc.viewer.availability.list.useQuery(undefined);
useEffect(
function refactorMeWithoutEffect() {
if (!data) {
return;
}
const schedules = data.schedules;
const options = schedules.map((schedule) => ({
value: schedule.id,
label: schedule.name,
@@ -225,8 +232,8 @@ const EventTypeSchedule = ({ eventType }: { eventType: EventTypeSetup }) => {
setValue("availability", value);
},
});
[data]
);
const availabilityValue = watch("availability");
useEffect(() => {
@@ -241,8 +248,8 @@ const EventTypeSchedule = ({ eventType }: { eventType: EventTypeSetup }) => {
{t("availability")}
{shouldLockIndicator("availability")}
</label>
{isLoading && <SelectSkeletonLoader />}
{!isLoading && (
{isPending && <SelectSkeletonLoader />}
{!isPending && (
<Controller
name="schedule"
render={({ field }) => {
@@ -446,7 +446,7 @@ function EventTypeSingleLayout({
</Suspense>
<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
<ConfirmationDialogContent
isLoading={deleteMutation.isLoading}
isPending={deleteMutation.isPending}
variety="danger"
title={t(`delete${isManagedEvent}_event_type`)}
confirmBtnText={t(`confirm_delete_event_type`)}
@@ -23,7 +23,7 @@ export const EventWebhooksTab = ({ eventType }: Pick<EventTypeSetupProps, "event
const { data: webhooks } = trpc.viewer.webhook.list.useQuery({ eventTypeId: eventType.id });
const { data: installedApps, isLoading } = trpc.viewer.integrations.useQuery({
const { data: installedApps, isPending } = trpc.viewer.integrations.useQuery({
variant: "other",
onlyInstalled: true,
});
@@ -103,7 +103,7 @@ export const EventWebhooksTab = ({ eventType }: Pick<EventTypeSetupProps, "event
return (
<div>
{webhooks && !isLoading && (
{webhooks && !isPending && (
<>
<div>
<div>
@@ -108,7 +108,7 @@ const InstantMeetingWebhooks = ({ eventType }: { eventType: EventTypeSetup }) =>
eventTypeId: eventType.id,
eventTriggers: [WebhookTriggerEvents.INSTANT_MEETING],
});
const { data: installedApps, isLoading } = trpc.viewer.integrations.useQuery({
const { data: installedApps, isPending } = trpc.viewer.integrations.useQuery({
variant: "other",
onlyInstalled: true,
});
@@ -188,7 +188,7 @@ const InstantMeetingWebhooks = ({ eventType }: { eventType: EventTypeSetup }) =>
return (
<div>
{webhooks && !isLoading && (
{webhooks && !isPending && (
<>
<div>
{webhooks.length ? (
@@ -8,18 +8,18 @@ interface ManagedEventDialogProps {
slugExistsChildrenDialogOpen: ChildrenEventType[];
slug: string;
onOpenChange: () => void;
isLoading: boolean;
isPending: boolean;
onConfirm: (e: { preventDefault: () => void }) => void;
}
export default function ManagedEventDialog(props: ManagedEventDialogProps) {
const { t } = useLocale();
const { slugExistsChildrenDialogOpen, slug, onOpenChange, isLoading, onConfirm } = props;
const { slugExistsChildrenDialogOpen, slug, onOpenChange, isPending, onConfirm } = props;
return (
<Dialog open={slugExistsChildrenDialogOpen.length > 0} onOpenChange={onOpenChange}>
<ConfirmationDialogContent
isLoading={isLoading}
isPending={isPending}
variety="warning"
title={t("managed_event_dialog_title", {
slug,
@@ -28,7 +28,7 @@ const AppConnectionItem = (props: IAppConnectionItem) => {
color="secondary"
disabled={installed}
type="button"
loading={buttonProps?.isLoading}
loading={buttonProps?.isPending}
onClick={(event) => {
// Save cookie key to return url step
document.cookie = `return-to=${window.location.href};path=/;max-age=3600;SameSite=Lax`;
@@ -31,7 +31,7 @@ const ConnectedCalendars = (props: IConnectCalendarsProps) => {
return (
<>
{/* Already connected calendars */}
{!queryConnectedCalendars.isLoading &&
{!queryConnectedCalendars.isPending &&
firstCalendar &&
firstCalendar.integration &&
firstCalendar.integration.title &&
@@ -76,7 +76,7 @@ const ConnectedCalendars = (props: IConnectCalendarsProps) => {
</List>
)}
{queryIntegrations.isLoading && <StepConnectionLoader />}
{queryIntegrations.isPending && <StepConnectionLoader />}
<button
type="button"
@@ -13,7 +13,7 @@ interface ConnectedAppStepProps {
const ConnectedVideoStep = (props: ConnectedAppStepProps) => {
const { nextStep } = props;
const { data: queryConnectedVideoApps, isLoading } = trpc.viewer.integrations.useQuery({
const { data: queryConnectedVideoApps, isPending } = trpc.viewer.integrations.useQuery({
variant: "conferencing",
onlyInstalled: false,
sortByMostPopular: true,
@@ -26,7 +26,7 @@ const ConnectedVideoStep = (props: ConnectedAppStepProps) => {
return (
<>
{!isLoading && (
{!isPending && (
<List className="bg-default border-subtle divide-subtle scroll-bar mx-1 max-h-[45vh] divide-y !overflow-y-scroll rounded-md border p-0 sm:mx-0">
{queryConnectedVideoApps?.items &&
queryConnectedVideoApps?.items.map((item) => {
@@ -48,7 +48,7 @@ const ConnectedVideoStep = (props: ConnectedAppStepProps) => {
</List>
)}
{isLoading && <StepConnectionLoader />}
{isPending && <StepConnectionLoader />}
<button
type="button"
data-testid="save-video-button"
@@ -113,7 +113,7 @@ const UserSettings = (props: IUserSettingsProps) => {
<Button
type="submit"
className="mt-8 flex w-full flex-row justify-center"
disabled={mutation.isLoading}>
disabled={mutation.isPending}>
{t("next_step_text")}
<ArrowRight className="ml-2 h-4 w-4 self-center" aria-hidden="true" />
</Button>
+2 -2
View File
@@ -7,7 +7,7 @@ import { Check } from "@calcom/ui/components/icon";
const StepDone = (props: {
currentStep: number;
nextStepPath: string;
setIsLoading: Dispatch<SetStateAction<boolean>>;
setIsPending: Dispatch<SetStateAction<boolean>>;
}) => {
const router = useRouter();
const { t } = useLocale();
@@ -18,7 +18,7 @@ const StepDone = (props: {
name={`wizard-step-${props.currentStep}`}
className="flex justify-center space-y-4"
onSubmit={(e) => {
props.setIsLoading(true);
props.setIsPending(true);
e.preventDefault();
router.replace(props.nextStepPath);
}}>
@@ -293,7 +293,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
{usernameChangeCondition === UsernameChangeStatusEnum.UPGRADE && (
<Button
type="button"
loading={updateUsername.isLoading}
loading={updateUsername.isPending}
data-testid="go-to-billing"
href={paymentLink}>
<>
@@ -305,7 +305,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
{usernameChangeCondition !== UsernameChangeStatusEnum.UPGRADE && (
<Button
type="button"
loading={updateUsername.isLoading}
loading={updateUsername.isPending}
data-testid="save-username"
onClick={() => {
saveUsername();
@@ -177,7 +177,7 @@ const UsernameTextfield = (props: ICustomUsernameProps & Partial<React.Component
<DialogFooter className="mt-4">
<Button
type="button"
loading={updateUsernameMutation.isLoading}
loading={updateUsernameMutation.isPending}
data-testid="save-username"
onClick={updateUsername}>
{t("save")}
+8 -36
View File
@@ -1,21 +1,15 @@
import type {
QueryObserverLoadingErrorResult,
QueryObserverLoadingResult,
QueryObserverPendingResult,
QueryObserverRefetchErrorResult,
QueryObserverSuccessResult,
QueryObserverLoadingErrorResult,
UseQueryResult,
} from "@tanstack/react-query";
import type { ReactNode } from "react";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { TRPCClientErrorLike } from "@calcom/trpc/client";
import type { DecorateProcedure } from "@calcom/trpc/react/shared";
import type { AnyQueryProcedure, inferProcedureInput, inferProcedureOutput } from "@calcom/trpc/server";
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
import { Alert, Loader } from "@calcom/ui";
import type { UseTRPCQueryOptions } from "@trpc/react-query/shared";
type ErrorLike = {
message: string;
};
@@ -27,7 +21,7 @@ interface QueryCellOptionsBase<TData, TError extends ErrorLike> {
error?: (
query: QueryObserverLoadingErrorResult<TData, TError> | QueryObserverRefetchErrorResult<TData, TError>
) => JSXElementOrNull;
loading?: (query: QueryObserverLoadingResult<TData, TError> | null) => JSXElementOrNull;
loading?: (query: QueryObserverPendingResult<TData, TError> | null) => JSXElementOrNull;
}
interface QueryCellOptionsNoEmpty<TData, TError extends ErrorLike>
@@ -58,8 +52,11 @@ export function QueryCell<TData, TError extends ErrorLike>(
const { isLocaleReady } = useLocale();
const StatusLoader = opts.customLoader || <Loader />; // Fixes edge case where this can return null form query cell
if (query.status === "loading" || !isLocaleReady) {
return opts.loading?.(query.status === "loading" ? query : null) ?? StatusLoader;
if (!isLocaleReady) {
return opts.loading?.(query.status === "pending" ? query : null) ?? StatusLoader;
}
if (query.status === "pending") {
return opts.loading?.(query) ?? StatusLoader;
}
if (query.status === "success") {
@@ -81,28 +78,3 @@ export function QueryCell<TData, TError extends ErrorLike>(
// impossible state
return null;
}
type TError = TRPCClientErrorLike<AppRouter>;
const withQuery = <
TQuery extends AnyQueryProcedure,
TInput = inferProcedureInput<TQuery>,
TOutput = inferProcedureOutput<TQuery>
>(
queryProcedure: DecorateProcedure<TQuery, inferProcedureInput<TQuery>, inferProcedureOutput<TQuery>>,
input?: TInput,
params?: UseTRPCQueryOptions<TQuery, TInput, TOutput, TOutput, TError>
) => {
return function WithQuery(
opts: Omit<
Partial<QueryCellOptionsWithEmpty<TOutput, TError>> & QueryCellOptionsNoEmpty<TOutput, TError>,
"query"
>
) {
const query = queryProcedure.useQuery(input, params);
return <QueryCell query={query} {...opts} />;
};
};
export { withQuery };
+1 -1
View File
@@ -60,7 +60,7 @@
"@sentry/nextjs": "^7.73.0",
"@stripe/react-stripe-js": "^1.10.0",
"@stripe/stripe-js": "^1.35.0",
"@tanstack/react-query": "^4.3.9",
"@tanstack/react-query": "^5.17.15",
"@tremor/react": "^2.0.0",
"@types/turndown": "^5.0.1",
"@upstash/ratelimit": "^0.4.3",
+11 -6
View File
@@ -5,7 +5,7 @@ import classNames from "classnames";
import { signIn } from "next-auth/react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { useState, useEffect } from "react";
import { FormProvider, useForm } from "react-hook-form";
import { FaGoogle } from "react-icons/fa";
import { z } from "zod";
@@ -157,15 +157,20 @@ inferSSRProps<typeof getServerSideProps> & WithNonceProps<{}>) {
else setErrorMessage(errorMessages[res.error] || t("something_went_wrong"));
};
const { data, isLoading } = trpc.viewer.public.ssoConnections.useQuery(undefined, {
onError: (err) => {
setErrorMessage(err.message);
const { data, isPending, error } = trpc.viewer.public.ssoConnections.useQuery();
useEffect(
function refactorMeWithoutEffect() {
if (error) {
setErrorMessage(error.message);
}
},
});
[error]
);
const displaySSOLogin = HOSTED_CAL_FEATURES
? true
: isSAMLLoginEnabled && !isLoading && data?.connectionExists;
: isSAMLLoginEnabled && !isPending && data?.connectionExists;
return (
<div className="dark:bg-brand dark:text-brand-contrast text-emphasis min-h-screen [--cal-brand-emphasis:#101010] [--cal-brand-subtle:#9CA3AF] [--cal-brand-text:white] [--cal-brand:#111827] dark:[--cal-brand-emphasis:#e1e1e1] dark:[--cal-brand-text:black] dark:[--cal-brand:white]">
+5 -5
View File
@@ -28,7 +28,7 @@ export default function Authorize() {
const [selectedAccount, setSelectedAccount] = useState<{ value: string; label: string } | null>();
const scopes = scope ? scope.toString().split(",") : [];
const { data: client, isLoading: isLoadingGetClient } = trpc.viewer.oAuth.getClient.useQuery(
const { data: client, isPending: isPendingGetClient } = trpc.viewer.oAuth.getClient.useQuery(
{
clientId: client_id as string,
},
@@ -37,7 +37,7 @@ export default function Authorize() {
}
);
const { data, isLoading: isLoadingProfiles } = trpc.viewer.teamsAndUserProfilesQuery.useQuery();
const { data, isPending: isPendingProfiles } = trpc.viewer.teamsAndUserProfilesQuery.useQuery();
const generateAuthCodeMutation = trpc.viewer.oAuth.generateAuthCode.useMutation({
onSuccess: (data) => {
@@ -58,7 +58,7 @@ export default function Authorize() {
if (mappedProfiles.length > 0) {
setSelectedAccount(mappedProfiles[0]);
}
}, [isLoadingProfiles]);
}, [isPendingProfiles]);
useEffect(() => {
if (status === "unauthenticated") {
@@ -69,9 +69,9 @@ export default function Authorize() {
}
}, [status]);
const isLoading = isLoadingGetClient || isLoadingProfiles || status !== "authenticated";
const isPending = isPendingGetClient || isPendingProfiles || status !== "authenticated";
if (isLoading) {
if (isPending) {
return <></>;
}
+9 -9
View File
@@ -41,16 +41,16 @@ export function Setup(props: inferSSRProps<typeof getServerSideProps>) {
{
title: t("administrator_user"),
description: t("lets_create_first_administrator_user"),
content: (setIsLoading) => (
content: (setIsPending) => (
<AdminUser
onSubmit={() => {
setIsLoading(true);
setIsPending(true);
}}
onSuccess={() => {
setStep(2);
}}
onError={() => {
setIsLoading(false);
setIsPending(false);
}}
userCount={props.userCount}
/>
@@ -59,7 +59,7 @@ export function Setup(props: inferSSRProps<typeof getServerSideProps>) {
{
title: t("choose_a_license"),
description: t("choose_license_description"),
content: (setIsLoading) => {
content: (setIsPending) => {
return (
<ChooseLicense
id="wizard-step-2"
@@ -67,7 +67,7 @@ export function Setup(props: inferSSRProps<typeof getServerSideProps>) {
value={value}
onChange={setValue}
onSubmit={() => {
setIsLoading(true);
setIsPending(true);
setStep(3);
}}
/>
@@ -80,14 +80,14 @@ export function Setup(props: inferSSRProps<typeof getServerSideProps>) {
steps.push({
title: t("step_enterprise_license"),
description: t("step_enterprise_license_description"),
content: (setIsLoading) => {
content: (setIsPending) => {
const currentStep = 3;
return (
<EnterpriseLicense
id={`wizard-step-${currentStep}`}
name={`wizard-step-${currentStep}`}
onSubmit={() => {
setIsLoading(true);
setIsPending(true);
}}
onSuccess={() => {
setStep(currentStep + 1);
@@ -106,7 +106,7 @@ export function Setup(props: inferSSRProps<typeof getServerSideProps>) {
title: t("enable_apps"),
description: t("enable_apps_description", { appName: APP_NAME }),
contentClassname: "!pb-0 mb-[-1px]",
content: (setIsLoading) => {
content: (setIsPending) => {
const currentStep = isFreeLicense ? 3 : 4;
return (
<AdminAppsList
@@ -120,7 +120,7 @@ export function Setup(props: inferSSRProps<typeof getServerSideProps>) {
baseURL={`/auth/setup?step=${currentStep}`}
useQueryParam={true}
onSubmit={() => {
setIsLoading(true);
setIsPending(true);
router.replace("/");
}}
/>
+1 -1
View File
@@ -44,7 +44,7 @@ function VerifyEmailPage() {
<Button
color="minimal"
className="underline"
loading={mutation.isLoading}
loading={mutation.isPending}
onClick={() => {
showToast("Send email", "success");
mutation.mutate();
+7 -7
View File
@@ -93,7 +93,7 @@ export default function Availability() {
const fromEventType = searchParams?.get("fromEventType");
const { timeFormat } = me.data || { timeFormat: null };
const [openSidebar, setOpenSidebar] = useState(false);
const { data: schedule, isLoading } = trpc.viewer.availability.schedule.get.useQuery(
const { data: schedule, isPending } = trpc.viewer.availability.schedule.get.useQuery(
{ scheduleId },
{
enabled: !!scheduleId,
@@ -158,7 +158,7 @@ export default function Availability() {
control={form.control}
name="name"
render={({ field }) => (
<EditableHeading isReady={!isLoading} {...field} data-testid="availablity-title" />
<EditableHeading isReady={!isPending} {...field} data-testid="availablity-title" />
)}
/>
}
@@ -188,7 +188,7 @@ export default function Availability() {
</Skeleton>
<Switch
id="hiddenSwitch"
disabled={isLoading || schedule?.isDefault}
disabled={isPending || schedule?.isDefault}
checked={form.watch("isDefault")}
onCheckedChange={(e) => {
form.setValue("isDefault", e);
@@ -210,7 +210,7 @@ export default function Availability() {
/>
</DialogTrigger>
<ConfirmationDialogContent
isLoading={deleteMutation.isLoading}
isPending={deleteMutation.isPending}
variety="danger"
title={t("delete_schedule")}
confirmBtnText={t("delete")}
@@ -249,7 +249,7 @@ export default function Availability() {
/>
</DialogTrigger>
<ConfirmationDialogContent
isLoading={deleteMutation.isLoading}
isPending={deleteMutation.isPending}
variety="danger"
title={t("delete_schedule")}
confirmBtnText={t("delete")}
@@ -284,7 +284,7 @@ export default function Availability() {
</Skeleton>
<Switch
id="hiddenSwitch"
disabled={isLoading || schedule?.isDefault}
disabled={isPending || schedule?.isDefault}
checked={form.watch("isDefault")}
onCheckedChange={(e) => {
form.setValue("isDefault", e);
@@ -334,7 +334,7 @@ export default function Availability() {
className="ml-4 lg:ml-0"
type="submit"
form="availability-form"
loading={updateMutation.isLoading}>
loading={updateMutation.isPending}>
{t("save")}
</Button>
<Button
+13 -11
View File
@@ -15,7 +15,7 @@ import { trpc } from "@calcom/trpc/react";
import { EmptyScreen, showToast, ToggleGroup } from "@calcom/ui";
import { Clock } from "@calcom/ui/components/icon";
import { withQuery } from "@lib/QueryCell";
import { QueryCell } from "@lib/QueryCell";
import PageWrapper from "@components/PageWrapper";
import SkeletonLoader from "@components/availability/SkeletonLoader";
@@ -136,8 +136,17 @@ export function AvailabilityList({ schedules }: RouterOutputs["viewer"]["availab
);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const WithQuery = withQuery(trpc.viewer.availability.list as any);
function AvailabilityListWithQuery() {
const query = trpc.viewer.availability.list.useQuery();
return (
<QueryCell
query={query}
success={({ data }) => <AvailabilityList {...data} />}
customLoader={<SkeletonLoader />}
/>
);
}
export default function AvailabilityPage() {
const { t } = useLocale();
@@ -179,14 +188,7 @@ export default function AvailabilityPage() {
<NewScheduleButton />
</div>
}>
{searchParams?.get("type") === "team" ? (
<AvailabilitySliderTable />
) : (
<WithQuery
success={({ data }) => <AvailabilityList {...data} />}
customLoader={<SkeletonLoader />}
/>
)}
{searchParams?.get("type") === "team" ? <AvailabilitySliderTable /> : <AvailabilityListWithQuery />}
</ShellMain>
</div>
);
+1 -1
View File
@@ -164,7 +164,7 @@ export default function Bookings() {
{query.status === "error" && (
<Alert severity="error" title={t("something_went_wrong")} message={query.error.message} />
)}
{(query.status === "loading" || query.isPaused) && <SkeletonLoader />}
{(query.status === "pending" || query.isPaused) && <SkeletonLoader />}
{query.status === "success" && !isEmpty && (
<>
{!!bookingsToday.length && status === "upcoming" && (
+1 -1
View File
@@ -68,7 +68,7 @@ function ConnectAndJoin() {
</div>
) : (
<Button
loading={mutation.isLoading}
loading={mutation.isPending}
tooltip={isUserPartOfOrg ? t("join_meeting") : t("not_part_of_org")}
disabled={!isUserPartOfOrg}
onClick={() => {
+2 -2
View File
@@ -552,7 +552,7 @@ const EventTypePage = (props: EventTypeSetupProps) => {
eventType={eventType}
team={team}
availability={availability}
isUpdateMutationLoading={updateMutation.isLoading}
isUpdateMutationLoading={updateMutation.isPending}
formMethods={formMethods}
// disableBorder={tabName === "apps" || tabName === "workflows" || tabName === "webhooks"}
disableBorder={true}
@@ -647,7 +647,7 @@ const EventTypePage = (props: EventTypeSetupProps) => {
{slugExistsChildrenDialogOpen.length ? (
<ManagedEventTypeDialog
slugExistsChildrenDialogOpen={slugExistsChildrenDialogOpen}
isLoading={formMethods.formState.isSubmitting}
isPending={formMethods.formState.isSubmitting}
onOpenChange={() => {
setSlugExistsChildrenDialogOpen([]);
}}
+3 -3
View File
@@ -686,7 +686,7 @@ export const EventTypeList = ({
title={t(`delete${isManagedEventPrefix()}_event_type`)}
confirmBtnText={t(`confirm_delete_event_type`)}
loadingText={t(`confirm_delete_event_type`)}
isLoading={deleteMutation.isLoading}
isPending={deleteMutation.isPending}
onConfirm={(e) => {
e.preventDefault();
deleteEventTypeHandler(deleteDialogTypeId);
@@ -856,7 +856,7 @@ const Main = ({
const searchParams = useCompatSearchParams();
const orgBranding = useOrgBranding();
if (!data || status === "loading") {
if (!data || status === "pending") {
return <SkeletonLoader />;
}
@@ -934,7 +934,7 @@ const EventTypesPage = () => {
// TODO: Maybe useSuspenseQuery to focus on success case only? Remember that it would crash the page when there is an error in query. Also, it won't support skeleton
const { data, status, error } = trpc.viewer.eventTypes.getByViewer.useQuery(filters && { filters }, {
refetchOnWindowFocus: false,
cacheTime: 1 * 60 * 60 * 1000,
gcTime: 1 * 60 * 60 * 1000,
staleTime: 1 * 60 * 60 * 1000,
});
@@ -36,7 +36,7 @@ const SkeletonLoader = ({ title, description }: { title: string; description: st
const ApiKeysView = () => {
const { t } = useLocale();
const { data, isLoading } = trpc.viewer.apiKeys.list.useQuery();
const { data, isPending } = trpc.viewer.apiKeys.list.useQuery();
const [apiKeyModal, setApiKeyModal] = useState(false);
const [apiKeyToEdit, setApiKeyToEdit] = useState<(TApiKeys & { neverExpires?: boolean }) | undefined>(
@@ -57,7 +57,7 @@ const ApiKeysView = () => {
);
};
if (isLoading || !data) {
if (isPending || !data) {
return (
<SkeletonLoader
title={t("api_keys")}
@@ -317,7 +317,7 @@ const AppearanceView = ({
<SettingsToggle
toggleSwitchAtTheEnd={true}
title={t("disable_cal_branding", { appName: APP_NAME })}
disabled={!hasPaidPlan || mutation?.isLoading}
disabled={!hasPaidPlan || mutation?.isPending}
description={t("removes_cal_branding", { appName: APP_NAME })}
checked={hasPaidPlan ? hideBrandingValue : false}
Badge={<UpgradeTeamsBadge />}
@@ -332,12 +332,12 @@ const AppearanceView = ({
};
const AppearanceViewWrapper = () => {
const { data: user, isLoading } = trpc.viewer.me.useQuery();
const { isLoading: isTeamPlanStatusLoading, hasPaidPlan } = useHasPaidPlan();
const { data: user, isPending } = trpc.viewer.me.useQuery();
const { isPending: isTeamPlanStatusLoading, hasPaidPlan } = useHasPaidPlan();
const { t } = useLocale();
if (isLoading || isTeamPlanStatusLoading || !user)
if (isPending || isTeamPlanStatusLoading || !user)
return <SkeletonLoader title={t("appearance")} description={t("appearance_description")} />;
return <AppearanceView user={user} hasPaidPlan={hasPaidPlan} />;
@@ -51,8 +51,8 @@ interface GeneralViewProps {
const GeneralQueryView = () => {
const { t } = useLocale();
const { data: user, isLoading } = trpc.viewer.me.useQuery();
if (isLoading) return <SkeletonLoader title={t("general")} description={t("general_description")} />;
const { data: user, isPending } = trpc.viewer.me.useQuery();
if (isPending) return <SkeletonLoader title={t("general")} description={t("general_description")} />;
if (!user) {
throw new Error(t("something_went_wrong"));
}
@@ -233,7 +233,7 @@ const GeneralView = ({ localeProp, user }: GeneralViewProps) => {
toggleSwitchAtTheEnd={true}
title={t("dynamic_booking")}
description={t("allow_dynamic_booking")}
disabled={mutation.isLoading}
disabled={mutation.isPending}
checked={isAllowDynamicBookingChecked}
onCheckedChange={(checked) => {
setIsAllowDynamicBookingChecked(checked);
@@ -246,7 +246,7 @@ const GeneralView = ({ localeProp, user }: GeneralViewProps) => {
toggleSwitchAtTheEnd={true}
title={t("seo_indexing")}
description={t("allow_seo_indexing")}
disabled={mutation.isLoading}
disabled={mutation.isPending}
checked={isAllowSEOIndexingChecked}
onCheckedChange={(checked) => {
setIsAllowSEOIndexingChecked(checked);
@@ -259,7 +259,7 @@ const GeneralView = ({ localeProp, user }: GeneralViewProps) => {
toggleSwitchAtTheEnd={true}
title={t("monthly_digest_email")}
description={t("monthly_digest_email_for_teams")}
disabled={mutation.isLoading}
disabled={mutation.isPending}
checked={isReceiveMonthlyDigestEmailChecked}
onCheckedChange={(checked) => {
setIsReceiveMonthlyDigestEmailChecked(checked);
@@ -155,7 +155,7 @@ const OutOfOfficeSection = () => {
<Button
color="primary"
type="submit"
disabled={createOutOfOfficeEntry.isLoading}
disabled={createOutOfOfficeEntry.isPending}
data-testid="create-entry-ooo-redirect">
{t("create_entry")}
</Button>
@@ -168,7 +168,7 @@ const OutOfOfficeSection = () => {
const OutOfOfficeEntriesList = () => {
const { t } = useLocale();
const utils = trpc.useContext();
const { data, isLoading } = trpc.viewer.outOfOfficeEntriesList.useQuery();
const { data, isPending } = trpc.viewer.outOfOfficeEntriesList.useQuery();
const deleteOutOfOfficeEntryMutation = trpc.viewer.outOfOfficeEntryDelete.useMutation({
onSuccess: () => {
showToast(t("success_deleted_entry_out_of_office"), "success");
@@ -207,7 +207,7 @@ const OutOfOfficeEntriesList = () => {
type="button"
color="minimal"
variant="icon"
disabled={deleteOutOfOfficeEntryMutation.isLoading}
disabled={deleteOutOfOfficeEntryMutation.isPending}
StartIcon={Trash2}
onClick={() => {
deleteOutOfOfficeEntryMutation.mutate({ outOfOfficeUid: item.uuid });
@@ -216,7 +216,7 @@ const OutOfOfficeEntriesList = () => {
</TableCell>
</TableRow>
))}
{isLoading && (
{isPending && (
<TableRow>
{new Array(6).fill(0).map((_, index) => (
<TableCell key={index}>
@@ -226,7 +226,7 @@ const OutOfOfficeEntriesList = () => {
</TableRow>
)}
{!isLoading && (data === undefined || data.length === 0) && (
{!isPending && (data === undefined || data.length === 0) && (
<TableRow>
<TableCell colSpan={6} className="text-center">
<p className="text-subtle text-sm">{t("no_redirects_found")}</p>
@@ -84,10 +84,10 @@ const ProfileView = () => {
const { t } = useLocale();
const utils = trpc.useContext();
const { update } = useSession();
const { data: user, isLoading } = trpc.viewer.me.useQuery();
const { data: user, isPending } = trpc.viewer.me.useQuery();
const { data: avatarData } = trpc.viewer.avatar.useQuery(undefined, {
enabled: !isLoading && !user?.avatarUrl,
enabled: !isPending && !user?.avatarUrl,
});
const updateProfileMutation = trpc.viewer.updateProfile.useMutation({
@@ -222,7 +222,7 @@ const ProfileView = () => {
[ErrorCode.ThirdPartyIdentityProviderEnabled]: t("account_created_with_identity_provider"),
};
if (isLoading || !user) {
if (isPending || !user) {
return (
<SkeletonLoader title={t("profile")} description={t("profile_description", { appName: APP_NAME })} />
);
@@ -246,7 +246,7 @@ const ProfileView = () => {
<ProfileForm
key={JSON.stringify(defaultValues)}
defaultValues={defaultValues}
isLoading={updateProfileMutation.isLoading}
isPending={updateProfileMutation.isPending}
isFallbackImg={!user.avatarUrl && !avatarData?.avatar}
user={user}
userOrganization={user.organization}
@@ -354,7 +354,7 @@ const ProfileView = () => {
<DialogFooter showDivider>
<Button
color="primary"
loading={confirmPasswordMutation.isLoading}
loading={confirmPasswordMutation.isPending}
onClick={(e) => onConfirmPassword(e)}>
{t("confirm")}
</Button>
@@ -375,7 +375,7 @@ const ProfileView = () => {
<DialogFooter>
<Button
color="primary"
loading={updateProfileMutation.isLoading}
loading={updateProfileMutation.isPending}
onClick={(e) => onConfirmAuthEmailChange(e)}>
{t("confirm")}
</Button>
@@ -391,7 +391,7 @@ const ProfileForm = ({
defaultValues,
onSubmit,
extraField,
isLoading = false,
isPending = false,
isFallbackImg,
user,
userOrganization,
@@ -399,7 +399,7 @@ const ProfileForm = ({
defaultValues: FormValues;
onSubmit: (values: FormValues) => void;
extraField?: React.ReactNode;
isLoading: boolean;
isPending: boolean;
isFallbackImg: boolean;
user: RouterOutputs["viewer"]["me"];
userOrganization: RouterOutputs["viewer"]["me"]["organization"];
@@ -509,7 +509,7 @@ const ProfileForm = ({
</div>
</div>
<SectionBottomActions align="end">
<Button loading={isLoading} disabled={isDisabled} color="primary" type="submit">
<Button loading={isPending} disabled={isDisabled} color="primary" type="submit">
{t("update")}
</Button>
</SectionBottomActions>
@@ -69,7 +69,7 @@ const ProfileImpersonationView = ({ user }: { user: RouterOutputs["viewer"]["me"
mutation.mutate({ disableImpersonation: !checked });
}}
switchContainerClassName="rounded-t-none border-t-0"
disabled={mutation.isLoading}
disabled={mutation.isPending}
/>
</div>
</>
@@ -77,10 +77,10 @@ const ProfileImpersonationView = ({ user }: { user: RouterOutputs["viewer"]["me"
};
const ProfileImpersonationViewWrapper = () => {
const { data: user, isLoading } = trpc.viewer.me.useQuery();
const { data: user, isPending } = trpc.viewer.me.useQuery();
const { t } = useLocale();
if (isLoading || !user)
if (isPending || !user)
return <SkeletonLoader title={t("impersonation")} description={t("impersonation_description")} />;
return <ProfileImpersonationView user={user} />;
@@ -215,9 +215,9 @@ const PasswordView = ({ user }: PasswordViewProps) => {
<Button
color="primary"
type="submit"
loading={passwordMutation.isLoading}
loading={passwordMutation.isPending}
onClick={() => formMethods.clearErrors("apiError")}
disabled={isDisabled || passwordMutation.isLoading || sessionMutation.isLoading}>
disabled={isDisabled || passwordMutation.isPending || sessionMutation.isPending}>
{t("update")}
</Button>
</SectionBottomActions>
@@ -268,7 +268,7 @@ const PasswordView = ({ user }: PasswordViewProps) => {
<SectionBottomActions align="end">
<Button
color="primary"
loading={sessionMutation.isLoading}
loading={sessionMutation.isPending}
onClick={() => {
sessionMutation.mutate({
metadata: { ...metadata, sessionTimeout },
@@ -277,8 +277,8 @@ const PasswordView = ({ user }: PasswordViewProps) => {
}}
disabled={
initialSessionTimeout === sessionTimeout ||
passwordMutation.isLoading ||
sessionMutation.isLoading
passwordMutation.isPending ||
sessionMutation.isPending
}>
{t("update")}
</Button>
@@ -293,9 +293,9 @@ const PasswordView = ({ user }: PasswordViewProps) => {
};
const PasswordViewWrapper = () => {
const { data: user, isLoading } = trpc.viewer.me.useQuery();
const { data: user, isPending } = trpc.viewer.me.useQuery();
const { t } = useLocale();
if (isLoading || !user)
if (isPending || !user)
return <SkeletonLoader title={t("password")} description={t("password_description")} />;
return <PasswordView user={user} />;
@@ -37,12 +37,12 @@ const TwoFactorAuthView = () => {
const utils = trpc.useContext();
const { t } = useLocale();
const { data: user, isLoading } = trpc.viewer.me.useQuery();
const { data: user, isPending } = trpc.viewer.me.useQuery();
const [enableModalOpen, setEnableModalOpen] = useState<boolean>(false);
const [disableModalOpen, setDisableModalOpen] = useState<boolean>(false);
if (isLoading)
if (isPending)
return <SkeletonLoader title={t("2fa")} description={t("set_up_two_factor_authentication")} />;
const isCalProvider = user?.identityProvider === "CAL";
+2 -2
View File
@@ -4,7 +4,7 @@ import superjson from "superjson";
import { CALCOM_VERSION } from "@calcom/lib/constants";
import prisma, { readonlyPrisma } from "@calcom/prisma";
import { createProxySSGHelpers } from "@calcom/trpc/react/ssg";
import { createServerSideHelpers } from "@calcom/trpc/react/server";
import { appRouter } from "@calcom/trpc/server/routers/_app";
// eslint-disable-next-line @typescript-eslint/no-var-requires
@@ -26,7 +26,7 @@ export async function ssgInit<TParams extends { locale?: string }>(opts: GetStat
const _i18n = await serverSideTranslations(locale, ["common"]);
const ssg = createProxySSGHelpers({
const ssg = createServerSideHelpers({
router: appRouter,
transformer: superjson,
ctx: {
+2 -2
View File
@@ -6,7 +6,7 @@ import { forms } from "@calcom/app-store/routing-forms/trpc/procedures/forms";
import { getLocale } from "@calcom/features/auth/lib/getLocale";
import { map } from "@calcom/features/flags/server/procedures/map";
import { CALCOM_VERSION } from "@calcom/lib/constants";
import { createProxySSGHelpers } from "@calcom/trpc/react/ssg";
import { createServerSideHelpers } from "@calcom/trpc/react/server";
import { createContext } from "@calcom/trpc/server/createContext";
import { me } from "@calcom/trpc/server/routers/loggedInViewer/procedures/me";
import { teamsAndUserProfilesQuery } from "@calcom/trpc/server/routers/loggedInViewer/procedures/teamsAndUserProfilesQuery";
@@ -59,7 +59,7 @@ export async function ssrInit(context: GetServerSidePropsContext, options?: { no
const locale = await getLocale(context.req);
const i18n = await serverSideTranslations(locale, ["common", "vital"]);
const ssr = createProxySSGHelpers({
const ssr = createServerSideHelpers({
router: routerSlice,
transformer: superjson,
ctx: { ...ctx, locale, i18n },
@@ -71,7 +71,7 @@ export default function OmniInstallAppButton({
return (
<Button
loading={mutation.isLoading}
loading={mutation.isPending}
color="secondary"
className="[@media(max-width:260px)]:w-full [@media(max-width:260px)]:justify-center"
StartIcon={Plus}
+54 -51
View File
@@ -32,64 +32,67 @@ function useAddAppMutation(_type: App["type"] | null, allOptions?: UseAddAppMuta
AddAppMutationData,
Error,
{ type?: App["type"]; variant?: string; slug?: string; isOmniInstall?: boolean; teamId?: number } | ""
>(async (variables) => {
let type: string | null | undefined;
let isOmniInstall;
const teamId = variables && variables.teamId ? variables.teamId : undefined;
if (variables === "") {
type = _type;
} else {
isOmniInstall = variables.isOmniInstall;
type = variables.type;
}
if (type?.endsWith("_other_calendar")) {
type = type.split("_other_calendar")[0];
}
>({
...options,
mutationFn: async (variables) => {
let type: string | null | undefined;
let isOmniInstall;
const teamId = variables && variables.teamId ? variables.teamId : undefined;
if (variables === "") {
type = _type;
} else {
isOmniInstall = variables.isOmniInstall;
type = variables.type;
}
if (type?.endsWith("_other_calendar")) {
type = type.split("_other_calendar")[0];
}
if (options?.installGoogleVideo && type !== "google_calendar")
throw new Error("Could not install Google Meet");
if (options?.installGoogleVideo && type !== "google_calendar")
throw new Error("Could not install Google Meet");
const state: IntegrationOAuthCallbackState = {
returnTo:
returnTo ||
WEBAPP_URL +
getInstalledAppPath(
{ variant: variables && variables.variant, slug: variables && variables.slug },
location.search
),
...(type === "google_calendar" && { installGoogleVideo: options?.installGoogleVideo }),
...(teamId && { teamId }),
};
const state: IntegrationOAuthCallbackState = {
returnTo:
returnTo ||
WEBAPP_URL +
getInstalledAppPath(
{ variant: variables && variables.variant, slug: variables && variables.slug },
location.search
),
...(type === "google_calendar" && { installGoogleVideo: options?.installGoogleVideo }),
...(teamId && { teamId }),
};
const stateStr = encodeURIComponent(JSON.stringify(state));
const searchParams = `?state=${stateStr}${teamId ? `&teamId=${teamId}` : ""}`;
const stateStr = encodeURIComponent(JSON.stringify(state));
const searchParams = `?state=${stateStr}${teamId ? `&teamId=${teamId}` : ""}`;
const res = await fetch(`/api/integrations/${type}/add${searchParams}`);
const res = await fetch(`/api/integrations/${type}/add${searchParams}`);
if (!res.ok) {
const errorBody = await res.json();
throw new Error(errorBody.message || "Something went wrong");
}
if (!res.ok) {
const errorBody = await res.json();
throw new Error(errorBody.message || "Something went wrong");
}
const json = await res.json();
const externalUrl = /https?:\/\//.test(json.url) && !json.url.startsWith(window.location.origin);
if (!isOmniInstall) {
gotoUrl(json.url, json.newTab);
return { setupPending: externalUrl || json.url.endsWith("/setup") };
}
// Skip redirection only if it is an OmniInstall and redirect URL isn't of some other origin
// This allows installation of apps like Stripe to still redirect to their authentication pages.
// Check first that the URL is absolute, then check that it is of different origin from the current.
if (externalUrl) {
// TODO: For Omni installation to authenticate and come back to the page where installation was initiated, some changes need to be done in all apps' add callbacks
gotoUrl(json.url, json.newTab);
return { setupPending: externalUrl };
}
const json = await res.json();
const externalUrl = /https?:\/\//.test(json.url) && !json.url.startsWith(window.location.origin);
if (!isOmniInstall) {
gotoUrl(json.url, json.newTab);
return { setupPending: externalUrl || json.url.endsWith("/setup") };
}
// Skip redirection only if it is an OmniInstall and redirect URL isn't of some other origin
// This allows installation of apps like Stripe to still redirect to their authentication pages.
// Check first that the URL is absolute, then check that it is of different origin from the current.
if (externalUrl) {
// TODO: For Omni installation to authenticate and come back to the page where installation was initiated, some changes need to be done in all apps' add callbacks
gotoUrl(json.url, json.newTab);
return { setupPending: externalUrl };
}
return { setupPending: externalUrl || json.url.endsWith("/setup") };
}, options);
},
});
return mutation;
}
@@ -121,7 +121,7 @@ function AlbySetupPage(props: IAlbySetupProps) {
});
}, [credentialId, props.clientId, props.clientSecret, saveKeysMutation]);
if (integrations.isLoading) {
if (integrations.isPending) {
return <div className="absolute z-50 flex h-screen w-full items-center bg-gray-200" />;
}
@@ -15,22 +15,26 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
const [selectedProject, setSelectedProject] = useState<undefined | { label: string; value: string }>();
const { data } = trpc.viewer.appBasecamp3.projects.useQuery();
const setProject = trpc.viewer.appBasecamp3.projectMutation.useMutation();
useEffect(() => {
setSelectedProject({
value: data?.projects.currentProject,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
label: data?.projects?.find((project: any) => project.id === data?.currentProject)?.name,
});
setProjects(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data?.projects?.map((project: any) => {
return {
value: project.id,
label: project.name,
};
})
);
}, [data]);
useEffect(
function refactorMeWithoutEffect() {
setSelectedProject({
value: data?.projects.currentProject,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
label: data?.projects?.find((project: any) => project.id === data?.currentProject)?.name,
});
setProjects(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data?.projects?.map((project: any) => {
return {
value: project.id,
label: project.name,
};
})
);
},
[data]
);
return (
<AppCard
+3 -3
View File
@@ -34,7 +34,7 @@ export const InstallAppButtonWithoutPlanCheck = (
onClick: () => {
mutation.mutate({ type: props.type });
},
loading: mutation.isLoading,
loading: mutation.isPending,
})}
</>
);
@@ -56,10 +56,10 @@ export const InstallAppButton = (
disableInstall?: boolean;
} & InstallAppButtonProps
) => {
const { isLoading: isUserLoading, data: user } = trpc.viewer.me.useQuery();
const { isPending: isUserLoading, data: user } = trpc.viewer.me.useQuery();
const router = useRouter();
const proProtectionElementRef = useRef<HTMLDivElement | null>(null);
const { isLoading: isTeamPlanStatusLoading, hasTeamPlan } = useHasTeamPlan();
const { isPending: isTeamPlanStatusLoading, hasTeamPlan } = useHasTeamPlan();
useEffect(() => {
const el = proProtectionElementRef.current;
@@ -11,7 +11,7 @@ export default function InstallAppButton(props: InstallAppButtonProps) {
onClick() {
mutation.mutate("");
},
loading: mutation.isLoading,
loading: mutation.isPending,
})}
</>
);
@@ -9,7 +9,7 @@ export default function InstallAppButton(props: InstallAppButtonProps) {
onClick() {
mutation.mutate("");
},
loading: mutation.isLoading,
loading: mutation.isPending,
})}
</>
);
@@ -23,15 +23,15 @@ export const SearchDialog = (props: ISearchDialog) => {
const [nextOffset, setNextOffset] = useState<number>(0);
const [keyword, setKeyword] = useState<string>("");
const { isOpenDialog, setIsOpenDialog } = props;
const [isLoading, setIsLoading] = useState(false);
const [isPending, setIsPending] = useState(false);
const [errorMessage, setErrorMessage] = useState("");
const [selectedMode, setSelectedMode] = useState<Mode>(MODE_SEARCH);
const searchGiphy = async (keyword: string, offset: number) => {
if (isLoading) {
if (isPending) {
return;
}
setIsLoading(true);
setIsPending(true);
setErrorMessage("");
const res = await fetch("/api/integrations/giphy/search", {
method: "POST",
@@ -53,15 +53,15 @@ export const SearchDialog = (props: ISearchDialog) => {
setErrorMessage("No Result found");
}
}
setIsLoading(false);
setIsPending(false);
return null;
};
const getGiphyByUrl = async (url: string) => {
if (isLoading) {
if (isPending) {
return;
}
setIsLoading(true);
setIsPending(true);
setErrorMessage("");
const res = await fetch("/api/integrations/giphy/get", {
method: "POST",
@@ -81,7 +81,7 @@ export const SearchDialog = (props: ISearchDialog) => {
setErrorMessage("No Result found");
}
}
setIsLoading(false);
setIsPending(false);
return null;
};
@@ -139,14 +139,14 @@ export const SearchDialog = (props: ISearchDialog) => {
}}
/>
</div>
<Button type="submit" tabIndex={-1} color="secondary" loading={isLoading}>
<Button type="submit" tabIndex={-1} color="secondary" loading={isPending}>
{t("search")}
</Button>
</form>
{gifImage && (
<div className="flex flex-col items-center space-x-2 space-y-2 pt-3 rtl:space-x-reverse">
<div className="bg-subtle flex w-full items-center justify-center">
{isLoading ? (
{isPending ? (
<div className="flex h-[200px] w-full items-center justify-center bg-gray-400 pb-3 pt-3">
<svg
className={classNames("mx-4 h-5 w-5 animate-spin", "text-inverted dark:text-emphasis")}
@@ -182,7 +182,7 @@ export const SearchDialog = (props: ISearchDialog) => {
size="sm"
color="secondary"
type="button"
loading={isLoading}
loading={isPending}
onClick={() => searchGiphy(keyword, nextOffset)}>
Shuffle
</Button>
@@ -59,7 +59,7 @@ export default function MakeSetup({ inviteLink }: InferGetServerSidePropsType<ty
setNewApiKeys({ ...newApiKeys, [teamId || ""]: apiKey });
}
if (integrations.isLoading) {
if (integrations.isPending) {
return <div className="bg-emphasis absolute z-50 flex h-screen w-full items-center" />;
}
@@ -27,7 +27,7 @@ export default function PayPalSetup() {
},
});
if (integrations.isLoading) {
if (integrations.isPending) {
return <div className="absolute z-50 flex h-screen w-full items-center bg-gray-200" />;
}
@@ -147,7 +147,7 @@ function NewFormDialog({ appUrl }: { appUrl: string }) {
</div>
<DialogFooter showDivider className="mt-12">
<DialogClose />
<Button loading={mutation.isLoading} data-testid="add-form" type="submit">
<Button loading={mutation.isPending} data-testid="add-form" type="submit">
{t("continue")}
</Button>
</DialogFooter>
@@ -234,7 +234,7 @@ function Dialogs({
<RoutingFormEmbedDialog />
<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
<ConfirmationDialogContent
isLoading={deleteMutation.isLoading}
isPending={deleteMutation.isPending}
variety="danger"
title={t("delete_form")}
confirmBtnText={t("delete_form_action")}
@@ -264,12 +264,12 @@ const actionsCtx = createContext({
_delete: {
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
onAction: (_arg: { routingForm: RoutingForm | null }) => {},
isLoading: false,
isPending: false,
},
toggle: {
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
onAction: (_arg: { routingForm: RoutingForm | null; checked: boolean }) => {},
isLoading: false,
isPending: false,
},
});
@@ -332,7 +332,7 @@ export function FormActionsProvider({ appUrl, children }: { appUrl: string; chil
setDeleteDialogOpen(true);
setDeleteDialogFormId(routingForm.id);
},
isLoading: false,
isPending: false,
},
toggle: {
onAction: ({ routingForm, checked }) => {
@@ -344,7 +344,7 @@ export function FormActionsProvider({ appUrl, children }: { appUrl: string; chil
disabled: !checked,
});
},
isLoading: toggleMutation.isLoading,
isPending: toggleMutation.isPending,
},
}}>
{children}
@@ -446,7 +446,7 @@ export const FormAction = forwardRef(function FormAction<T extends typeof Button
},
_delete: {
onClick: () => _delete.onAction({ routingForm }),
loading: _delete.isLoading,
loading: _delete.isPending,
},
create: {
onClick: () => openModal({ action: "new", target: "" }),
@@ -479,7 +479,7 @@ export const FormAction = forwardRef(function FormAction<T extends typeof Button
</div>
);
},
loading: toggle.isLoading,
loading: toggle.isPending,
},
};
@@ -65,7 +65,7 @@ const Actions = ({
}: {
form: RoutingFormWithResponseCount;
mutation: {
isLoading: boolean;
isPending: boolean;
};
}) => {
const { t } = useLocale();
@@ -218,7 +218,7 @@ const Actions = ({
</FormActionsDropdown>
</div>
<VerticalDivider />
<Button data-testid="update-form" loading={mutation.isLoading} type="submit" color="primary">
<Button data-testid="update-form" loading={mutation.isPending} type="submit" color="primary">
{t("save")}
</Button>
</div>
@@ -518,7 +518,7 @@ function SingleForm({ form, appUrl, Page }: SingleFormComponentProps) {
}
export default function SingleFormWrapper({ form: _form, ...props }: SingleFormComponentProps) {
const { data: form, isLoading } = trpc.viewer.appRoutingForms.formQuery.useQuery(
const { data: form, isPending } = trpc.viewer.appRoutingForms.formQuery.useQuery(
{ id: _form.id },
{
initialData: _form,
@@ -527,7 +527,7 @@ export default function SingleFormWrapper({ form: _form, ...props }: SingleFormC
);
const { t } = useLocale();
if (isLoading) {
if (isPending) {
// It shouldn't be possible because we are passing the data from SSR to it as initialData. So, no need for skeleton here
return null;
}
@@ -34,7 +34,7 @@ type QueryBuilderUpdatedConfig = typeof QueryBuilderInitialConfig & { fields: Co
const Result = ({ formId, jsonLogicQuery }: { formId: string; jsonLogicQuery: JsonLogicQuery | null }) => {
const { t } = useLocale();
const { isLoading, status, data, isFetching, error, isFetchingNextPage, hasNextPage, fetchNextPage } =
const { isPending, status, data, isFetching, error, isFetchingNextPage, hasNextPage, fetchNextPage } =
trpc.viewer.appRoutingForms.report.useInfiniteQuery(
{
formId: formId,
@@ -57,7 +57,7 @@ const Result = ({ formId, jsonLogicQuery }: { formId: string; jsonLogicQuery: Js
const headers = useRef<string[] | null>(null);
if (!isLoading && !data) {
if (!isPending && !data) {
return <div>Error loading report {error?.message} </div>;
}
headers.current = (data?.pages && data?.pages[0]?.headers) || headers.current;
@@ -81,7 +81,7 @@ const Result = ({ formId, jsonLogicQuery }: { formId: string; jsonLogicQuery: Js
</th>
))}
</tr>
{!isLoading &&
{!isPending &&
data?.pages.map((page) => {
return page.responses?.map((responses, rowIndex) => {
const isLastRow = page.responses.length - 1 === rowIndex;
@@ -113,7 +113,7 @@ const Result = ({ formId, jsonLogicQuery }: { formId: string; jsonLogicQuery: Js
});
})}
</table>
{isLoading ? <div className="text-default p-2">{t("loading")}</div> : ""}
{isPending ? <div className="text-default p-2">{t("loading")}</div> : ""}
{hasNextPage && (
<Button
type="button"
@@ -153,7 +153,7 @@ function RoutingForm({ form, profile, ...restProps }: Props) {
<div className="mt-4 flex justify-end space-x-2 rtl:space-x-reverse">
<Button
className="dark:bg-darkmodebrand dark:text-darkmodebrandcontrast dark:hover:border-darkmodebrandcontrast dark:border-transparent"
loading={responseMutation.isLoading}
loading={responseMutation.isPending}
type="submit"
color="primary">
{t("submit")}
+1 -1
View File
@@ -35,7 +35,7 @@ export interface InstallAppButtonProps {
renderProps: ButtonProps & {
/** Tells that the default render component should be used */
useDefaultComponent?: boolean;
isLoading?: boolean;
isPending?: boolean;
}
) => JSX.Element;
onChanged?: () => unknown;
@@ -44,7 +44,7 @@ const wipeMyCalAction = async (props: IWipeMyCalAction) => {
export const ConfirmDialog = (props: IConfirmDialogWipe) => {
const { t } = useLocale();
const { isOpenDialog, setIsOpenDialog } = props;
const [isLoading, setIsLoading] = useState(false);
const [isPending, setIsPending] = useState(false);
const today = dayjs();
const initialDate = today.startOf("day");
const endDate = today.endOf("day");
@@ -52,9 +52,9 @@ export const ConfirmDialog = (props: IConfirmDialogWipe) => {
const utils = trpc.useContext();
const rescheduleApi = useMutation(
async () => {
setIsLoading(true);
const rescheduleApi = useMutation({
mutationFn: async () => {
setIsPending(true);
try {
const result = await wipeMyCalAction({
initialDate: initialDate.toISOString(),
@@ -68,14 +68,12 @@ export const ConfirmDialog = (props: IConfirmDialogWipe) => {
showToast(t("unexpected_error_try_again"), "error");
// @TODO: notify
}
setIsLoading(false);
setIsPending(false);
},
{
async onSettled() {
await utils.viewer.bookings.invalidate();
},
}
);
async onSettled() {
await utils.viewer.bookings.invalidate();
},
});
return (
<Dialog open={isOpenDialog} onOpenChange={setIsOpenDialog}>
@@ -104,7 +102,7 @@ export const ConfirmDialog = (props: IConfirmDialogWipe) => {
<Button
color="primary"
data-testid="send_request"
disabled={isLoading}
disabled={isPending}
onClick={async () => {
try {
rescheduleApi.mutate();
@@ -13,7 +13,7 @@ interface IWipeMyCalActionButtonProps {
const WipeMyCalActionButton = (props: IWipeMyCalActionButtonProps) => {
const { bookingsEmpty, bookingStatus } = props;
const [openDialog, setOpenDialog] = useState(false);
const { isSuccess, isLoading, data } = trpc.viewer.integrations.useQuery({
const { isSuccess, isPending, data } = trpc.viewer.integrations.useQuery({
variant: "other",
onlyInstalled: undefined,
});
@@ -27,7 +27,7 @@ const WipeMyCalActionButton = (props: IWipeMyCalActionButtonProps) => {
return (
<>
{data && isSuccess && !isLoading && credentialId && (
{data && isSuccess && !isPending && credentialId && (
<div className="mb-4">
<ConfirmDialog isOpenDialog={openDialog} setIsOpenDialog={setOpenDialog} />
<Button color="primary" onClick={() => setOpenDialog(true)} data-testid="wipe-today-button">
@@ -60,7 +60,7 @@ export default function ZapierSetup(props: IZapierSetupProps) {
setNewApiKeys({ ...newApiKeys, [teamId || ""]: apiKey });
}
if (integrations.isLoading) {
if (integrations.isPending) {
return <div className="bg-emphasis absolute z-50 flex h-screen w-full items-center" />;
}
+2 -2
View File
@@ -270,7 +270,7 @@ const AdminAppsListContainer = () => {
const { t } = useLocale();
const category = searchParams?.get("category") || AppCategories.calendar;
const { data: apps, isLoading } = trpc.viewer.appsRouter.listLocal.useQuery(
const { data: apps, isPending } = trpc.viewer.appsRouter.listLocal.useQuery(
{ category },
{ enabled: searchParams !== null }
);
@@ -291,7 +291,7 @@ const AdminAppsListContainer = () => {
const handleModelOpen = (data: EditModalState) => setModalState({ ...data });
if (isLoading) return <SkeletonLoader />;
if (isPending) return <SkeletonLoader />;
if (!apps || apps.length === 0) {
return (
+4 -4
View File
@@ -207,7 +207,7 @@ const BookerComponent = ({
} = useCalendars();
useEffect(() => {
if (event.isLoading) return setBookerState("loading");
if (event.isPending) return setBookerState("loading");
if (!selectedDate) return setBookerState("selecting_date");
if (!selectedTimeslot) return setBookerState("selecting_time");
return setBookerState("booking");
@@ -399,7 +399,7 @@ const BookerComponent = ({
<BookerSection
area="meta"
className="max-w-screen flex w-full flex-col md:w-[var(--booker-meta-width)]">
<EventMeta event={event.data} isLoading={event.isLoading} />
<EventMeta event={event.data} isPending={event.isPending} />
{layout !== BookerLayouts.MONTH_VIEW &&
!(layout === "mobile" && bookerState === "booking") && (
<div className="mt-auto px-5 py-3 ">
@@ -434,7 +434,7 @@ const BookerComponent = ({
visible={layout === BookerLayouts.WEEK_VIEW}
className="border-subtle sticky top-0 ml-[-1px] h-full md:border-l"
{...fadeInLeft}>
<LargeCalendar extraDays={extraDays} schedule={schedule.data} isLoading={schedule.isLoading} />
<LargeCalendar extraDays={extraDays} schedule={schedule.data} isLoading={schedule.isPending} />
</BookerSection>
<BookerSection
@@ -456,7 +456,7 @@ const BookerComponent = ({
extraDays={extraDays}
limitHeight={layout === BookerLayouts.MONTH_VIEW}
schedule={schedule?.data}
isLoading={schedule.isLoading}
isLoading={schedule.isPending}
seatsPerTimeSlot={event.data?.seatsPerTimeSlot}
showAvailableSeatsCount={event.data?.seatsShowAvailabilityCount}
/>
@@ -63,7 +63,7 @@ export const BookEventForm = ({
}, [eventType]);
if (eventQuery.isError) return <Alert severity="warning" message={t("error_booking_event")} />;
if (eventQuery.isLoading || !eventQuery.data) return <FormSkeleton />;
if (eventQuery.isPending || !eventQuery.data) return <FormSkeleton />;
if (!timeslot)
return (
<EmptyScreen
@@ -27,7 +27,7 @@ export const DatePicker = ({
return (
<DatePickerComponent
isLoading={schedule.isLoading}
isPending={schedule.isPending}
onChange={(date: Dayjs | null) => {
setSelectedDate(date === null ? date : date.format("YYYY-MM-DD"));
}}
@@ -24,10 +24,10 @@ const TimezoneSelect = dynamic(
export const EventMeta = ({
event,
isLoading,
isPending,
}: {
event: useEventReturnType["data"];
isLoading: useEventReturnType["isLoading"];
isPending: useEventReturnType["isPending"];
}) => {
const { setTimezone, timeFormat, timezone } = useTimePreferences();
const selectedDuration = useBookerStore((state) => state.selectedDuration);
@@ -40,7 +40,6 @@ export const EventMeta = ({
shallow
);
const { i18n, t } = useLocale();
const embedUiConfig = useEmbedUiConfig();
const isEmbed = useIsEmbed();
const hideEventTypeDetails = isEmbed ? embedUiConfig.hideEventTypeDetails : false;
@@ -66,12 +65,12 @@ export const EventMeta = ({
return (
<div className="relative z-10 p-6" data-testid="event-meta">
{isLoading && (
{isPending && (
<m.div {...fadeInUp} initial="visible" layout>
<EventMetaSkeleton />
</m.div>
)}
{!isLoading && !!event && (
{!isPending && !!event && (
<m.div {...fadeInUp} layout transition={{ ...fadeInUp.transition, delay: 0.3 }}>
<EventMembers
schedulingType={event.schedulingType}
@@ -74,7 +74,7 @@ export const LargeCalendar = ({
return (
<div className="h-full [--calendar-dates-sticky-offset:66px]">
<Calendar
isLoading={isLoading}
isPending={isLoading}
availableTimeslots={availableSlots}
startHour={0}
endHour={23}
@@ -1,6 +1,6 @@
import { useMutation } from "@tanstack/react-query";
import { useRouter, useSearchParams } from "next/navigation";
import { useRef, useState } from "react";
import { useRef, useState, useEffect } from "react";
import { createPaymentLink } from "@calcom/app-store/stripepayment/lib/client";
import dayjs from "@calcom/dayjs";
@@ -74,27 +74,33 @@ export const useBookings = ({ event, hashedLink, bookingForm }: IUseBookings) =>
enabled: !!bookingId && !hasInstantMeetingTokenExpired,
refetchInterval: 2000,
refetchIntervalInBackground: true,
onSuccess: (data) => {
try {
showToast(t("something_went_wrong_on_our_end"), "error");
const locationVideoCallUrl: string | undefined = bookingMetadataSchema.parse(
data.booking?.metadata || {}
)?.videoCallUrl;
if (locationVideoCallUrl) {
router.push(locationVideoCallUrl);
} else {
showToast(t("something_went_wrong_on_our_end"), "error");
}
} catch (err) {
showToast(t("something_went_wrong_on_our_end"), "error");
}
},
}
);
useEffect(
function refactorMeWithoutEffect() {
const data = _instantBooking.data;
if (!data) return;
try {
showToast(t("something_went_wrong_on_our_end"), "error");
const createBookingMutation = useMutation(createBooking, {
const locationVideoCallUrl: string | undefined = bookingMetadataSchema.parse(
data.booking?.metadata || {}
)?.videoCallUrl;
if (locationVideoCallUrl) {
router.push(locationVideoCallUrl);
} else {
showToast(t("something_went_wrong_on_our_end"), "error");
}
} catch (err) {
showToast(t("something_went_wrong_on_our_end"), "error");
}
},
[_instantBooking.data]
);
const createBookingMutation = useMutation({
mutationFn: createBooking,
onSuccess: (responseData) => {
const { uid, paymentUid } = responseData;
const fullName = getFullName(bookingForm.getValues("responses.name"));
@@ -141,7 +147,8 @@ export const useBookings = ({ event, hashedLink, bookingForm }: IUseBookings) =>
},
});
const createInstantBookingMutation = useMutation(createInstantBooking, {
const createInstantBookingMutation = useMutation({
mutationFn: createInstantBooking,
onSuccess: (responseData) => {
updateQueryParam("bookingId", responseData.bookingId);
setExpiryTime(responseData.expires);
@@ -153,7 +160,8 @@ export const useBookings = ({ event, hashedLink, bookingForm }: IUseBookings) =>
},
});
const createRecurringBookingMutation = useMutation(createRecurringBooking, {
const createRecurringBookingMutation = useMutation({
mutationFn: createRecurringBooking,
onSuccess: async (responseData) => {
const booking = responseData[0] || {};
const { uid } = booking;
@@ -253,10 +261,10 @@ export const useBookings = ({ event, hashedLink, bookingForm }: IUseBookings) =>
// A redirect is triggered on mutation success, so keep the loading state while it is happening.
const loadingStates = {
creatingBooking: createBookingMutation.isLoading || createBookingMutation.isSuccess,
creatingBooking: createBookingMutation.isPending || createBookingMutation.isSuccess,
creatingRecurringBooking:
createRecurringBookingMutation.isLoading || createRecurringBookingMutation.isSuccess,
creatingInstantBooking: createInstantBookingMutation.isLoading,
createRecurringBookingMutation.isPending || createRecurringBookingMutation.isSuccess,
creatingInstantBooking: createInstantBookingMutation.isPending,
};
return {
@@ -1,5 +1,6 @@
import { useSession } from "next-auth/react";
import { useSearchParams } from "next/navigation";
import { useEffect } from "react";
import { shallow } from "zustand/shallow";
import { useTimePreferences } from "@calcom/features/bookings/lib";
@@ -30,7 +31,7 @@ export const useCalendars = () => {
shallow
);
const { data: overlayBusyDates } = trpc.viewer.availability.calendarOverlay.useQuery(
const { data: overlayBusyDates, isError } = trpc.viewer.availability.calendarOverlay.useQuery(
{
loggedInUsersTz: timezone || "Europe/London",
dateFrom: selectedDate,
@@ -42,13 +43,18 @@ export const useCalendars = () => {
},
{
enabled: !!session && set.size > 0 && switchEnabled,
onError: () => {
clearSet();
},
}
);
const { data, isLoading } = trpc.viewer.connectedCalendars.useQuery(undefined, {
useEffect(
function refactorMeWithoutEffect() {
if (!isError) return;
clearSet();
},
[isError]
);
const { data, isPending } = trpc.viewer.connectedCalendars.useQuery(undefined, {
enabled: !!calendarSettingsOverlay || Boolean(searchParams?.get("overlayCalendar")),
});
@@ -56,7 +62,7 @@ export const useCalendars = () => {
overlayBusyDates,
isOverlayCalendarEnabled: switchEnabled,
connectedCalendars: data?.connectedCalendars || [],
loadingConnectedCalendar: isLoading,
loadingConnectedCalendar: isPending,
onToggleCalendar: () => {
utils.viewer.availability.calendarOverlay.reset();
},
@@ -1,5 +1,5 @@
import { useSession } from "next-auth/react";
import { Fragment, useState } from "react";
import { Fragment, useMemo } from "react";
import {
FilterCheckboxFieldsContainer,
@@ -36,24 +36,24 @@ export const EventTypeFilter = () => {
const { data: user } = useSession();
const { data: query, pushItemToKey, removeItemByKeyAndValue, removeAllQueryParams } = useFilterQuery();
const [groupedEventTypes, setGroupedEventTypes] = useState<GroupedEventTypeState>();
const eventTypes = trpc.viewer.eventTypes.listWithTeam.useQuery(undefined, {
onSuccess: (data) => {
// Will be handled up the tree to redirect
// Group event types by team
const grouped = groupBy<IEventTypeFilter>(
data.filter((el) => el.team),
(item) => item?.team?.name || ""
); // Add the team name
const individualEvents = data.filter((el) => !el.team);
// push indivdual events to the start of grouped array
setGroupedEventTypes(
individualEvents.length > 0 ? { user_own_event_types: individualEvents, ...grouped } : grouped
);
},
enabled: !!user,
});
const groupedEventTypes: GroupedEventTypeState | null = useMemo(() => {
const data = eventTypes.data;
if (!data) {
return null;
}
// Will be handled up the tree to redirect
// Group event types by team
const grouped = groupBy<IEventTypeFilter>(
data.filter((el) => el.team),
(item) => item?.team?.name || ""
); // Add the team name
const individualEvents = data.filter((el) => !el.team);
// push indivdual events to the start of grouped array
return individualEvents.length > 0 ? { user_own_event_types: individualEvents, ...grouped } : grouped;
}, [eventTypes.data]);
if (!eventTypes.data) return null;
const isEmpty = eventTypes.data.length === 0;
@@ -30,8 +30,8 @@ export const VerifyCodeDialog = ({
isUserSessionRequiredToVerify?: boolean;
}) => {
const { t } = useLocale();
// Not using the mutation isLoading flag because after verifying we submit the underlying org creation form
const [isLoading, setIsLoading] = useState(false);
// Not using the mutation isPending flag because after verifying we submit the underlying org creation form
const [isPending, setIsPending] = useState(false);
const [error, setError] = useState("");
const [value, setValue] = useState("");
const [hasVerified, setHasVerified] = useState(false);
@@ -49,11 +49,11 @@ export const VerifyCodeDialog = ({
const verifyCodeMutationUserSessionRequired = trpc.viewer.organizations.verifyCode.useMutation({
onSuccess: (data) => {
setIsLoading(false);
setIsPending(false);
onSuccess(data);
},
onError: (err) => {
setIsLoading(false);
setIsPending(false);
setHasVerified(false);
if (err.message === "invalid_code") {
setError(t("code_provided_invalid"));
@@ -63,11 +63,11 @@ export const VerifyCodeDialog = ({
const verifyCodeMutationUserSessionNotRequired = trpc.viewer.auth.verifyCodeUnAuthenticated.useMutation({
onSuccess: (data) => {
setIsLoading(false);
setIsPending(false);
onSuccess(data);
},
onError: (err) => {
setIsLoading(false);
setIsPending(false);
setHasVerified(false);
if (err.message === "invalid_code") {
setError(t("code_provided_invalid"));
@@ -77,7 +77,7 @@ export const VerifyCodeDialog = ({
const verifyCode = useCallback(() => {
setError("");
setIsLoading(true);
setIsPending(true);
if (isUserSessionRequiredToVerify) {
verifyCodeMutationUserSessionRequired.mutate({
code: value,
@@ -102,10 +102,10 @@ export const VerifyCodeDialog = ({
// trim the input value because "react-digit-input" creates a string of the given length,
// even when some digits are missing. And finally we use regex to check if the value consists
// of 6 non-empty digits.
if (hasVerified || error || isLoading || !/^\d{6}$/.test(value.trim())) return;
if (hasVerified || error || isPending || !/^\d{6}$/.test(value.trim())) return;
verifyCode();
}, [error, isLoading, value, verifyCode, hasVerified]);
}, [error, isPending, value, verifyCode, hasVerified]);
useEffect(() => setValue(""), [isOpenDialog]);
@@ -149,7 +149,7 @@ export const VerifyCodeDialog = ({
)}
<DialogFooter>
<DialogClose />
<Button type="submit" onClick={verifyCode} loading={isLoading}>
<Button type="submit" onClick={verifyCode} loading={isPending}>
{t("submit")}
</Button>
</DialogFooter>
+13 -21
View File
@@ -22,14 +22,8 @@ const CalendarSwitch = (props: ICalendarSwitchProps) => {
const [checkedInternal, setCheckedInternal] = useState(isChecked);
const utils = trpc.useContext();
const { t } = useLocale();
const mutation = useMutation<
unknown,
unknown,
{
isOn: boolean;
}
>(
async ({ isOn }: { isOn: boolean }) => {
const mutation = useMutation({
mutationFn: async ({ isOn }: { isOn: boolean }) => {
const body = {
integration: type,
externalId: externalId,
@@ -60,24 +54,22 @@ const CalendarSwitch = (props: ICalendarSwitchProps) => {
}
}
},
{
async onSettled() {
await utils.viewer.integrations.invalidate();
await utils.viewer.connectedCalendars.invalidate();
},
onError() {
setCheckedInternal(false);
showToast(`Something went wrong when toggling "${title}"`, "error");
},
}
);
async onSettled() {
await utils.viewer.integrations.invalidate();
await utils.viewer.connectedCalendars.invalidate();
},
onError() {
setCheckedInternal(false);
showToast(`Something went wrong when toggling "${title}"`, "error");
},
});
return (
<div className={classNames("my-2 flex flex-row items-center")}>
<div className="flex pl-2">
<Switch
id={externalId}
checked={checkedInternal}
disabled={mutation.isLoading}
disabled={mutation.isPending}
onCheckedChange={async (isOn: boolean) => {
setCheckedInternal(isOn);
await mutation.mutate({ isOn });
@@ -93,7 +85,7 @@ const CalendarSwitch = (props: ICalendarSwitchProps) => {
{t("adding_events_to")}
</span>
)}
{mutation.isLoading && <RotateCw className="text-muted h-4 w-4 animate-spin ltr:ml-1 rtl:mr-1" />}
{mutation.isPending && <RotateCw className="text-muted h-4 w-4 animate-spin ltr:ml-1 rtl:mr-1" />}
</div>
);
};
+3 -3
View File
@@ -36,7 +36,7 @@ export type DatePickerProps = {
/** allows adding classes to the container */
className?: string;
/** Shows a small loading spinner next to the month name */
isLoading?: boolean;
isPending?: boolean;
/** used to query the multiple selected dates */
eventSlug?: string;
};
@@ -206,7 +206,7 @@ const Days = ({
<div key={day === null ? `e-${idx}` : `day-${day.format()}`} className="relative w-full pt-[100%]">
{day === null ? (
<div key={`e-${idx}`} />
) : props.isLoading ? (
) : props.isPending ? (
<button
className="bg-muted text-muted absolute bottom-0 left-0 right-0 top-0 mx-auto flex w-full items-center justify-center rounded-sm border-transparent text-center font-medium opacity-50"
key={`e-${idx}`}
@@ -226,7 +226,7 @@ const Days = ({
</div>
))}
{!props.isLoading && includedDates && includedDates?.length === 0 && (
{!props.isPending && includedDates && includedDates?.length === 0 && (
<NoAvailabilityOverlay month={month} nextMonthButton={nextMonthButton} />
)}
</>
@@ -11,7 +11,7 @@ import { Check } from "@calcom/ui/components/icon";
interface Props {
onChange: (value: { externalId: string; integration: string }) => void;
isLoading?: boolean;
isPending?: boolean;
hidePlaceholder?: boolean;
/** The external Id of the connected calendar */
destinationCalendar?: DestinationCalendar | null;
@@ -49,7 +49,7 @@ const OptionComponent = ({ ...props }: OptionProps<Option>) => {
const DestinationCalendarSelector = ({
onChange,
isLoading,
isPending,
value,
hidePlaceholder,
hideAdvancedText,
@@ -173,7 +173,7 @@ const DestinationCalendarSelector = ({
externalId,
});
}}
isLoading={isLoading}
isLoading={isPending}
value={selectedOption}
components={{ SingleValue: SingleValueComponent, Option: OptionComponent }}
isMulti={false}
@@ -43,7 +43,7 @@ DatePicker component offers a complete and customizable element for the user to
minDate: dayjs("2023-07-23").utc(),
maxDate: dayjs("2023-08-30").utc(),
selected: dayjs("2023-08-30").utc(),
isLoading: false,
isPending: false,
onChange: (newDate) => console.log(newDate),
onMonthChange: (newMonth) => console.log(newMonth),
}}
@@ -58,7 +58,7 @@ DatePicker component offers a complete and customizable element for the user to
control: "date"
}
}}>
{({ weekStart, excludedDates, onChange, onMonthChange, minDate, maxDate, selected, isLoading }) => (
{({ weekStart, excludedDates, onChange, onMonthChange, minDate, maxDate, selected, isPending }) => (
<VariantsTable titles={["Default"]} columnMinWidth={150}>
<VariantRow variant="Default">
<DatePicker
@@ -69,7 +69,7 @@ DatePicker component offers a complete and customizable element for the user to
onChange={onChange}
onMonthChange={onMonthChange}
selected={dayjs(selected)}
isLoading={isLoading}
isPending={isPending}
/>
</VariantRow>
</VariantsTable>
@@ -58,7 +58,7 @@ export function Calendar(props: CalendarComponentProps) {
} as React.CSSProperties // This can't live in the css file because it's a dynamic value and css variable gets super
}>
{hideHeader !== true && <SchedulerHeading />}
{props.isLoading && <Spinner />}
{props.isPending && <Spinner />}
<div
ref={container}
className="bg-default dark:bg-muted relative isolate flex h-full flex-auto flex-col">
@@ -121,6 +121,6 @@ export type CalendarState = {
hideHeader?: boolean;
};
export type CalendarComponentProps = CalendarPublicActions & CalendarState & { isLoading?: boolean };
export type CalendarComponentProps = CalendarPublicActions & CalendarState & { isPending?: boolean };
export type CalendarStoreProps = CalendarComponentProps & CalendarPrivateActions;
@@ -82,7 +82,7 @@ const ApiKeyListItem = ({
<DropdownItem
type="button"
color="destructive"
disabled={deleteApiKey.isLoading}
disabled={deleteApiKey.isPending}
onClick={() =>
deleteApiKey.mutate({
id: apiKey.id,
@@ -43,7 +43,7 @@ export const AboutOrganizationForm = () => {
form={aboutOrganizationFormMethods}
className="space-y-5"
handleSubmit={(v) => {
if (!updateOrganizationMutation.isLoading) {
if (!updateOrganizationMutation.isPending) {
setServerErrorMessage(null);
updateOrganizationMutation.mutate({ ...v, orgId });
}
@@ -109,7 +109,7 @@ export const AboutOrganizationForm = () => {
<div className="flex">
<Button
disabled={
aboutOrganizationFormMethods.formState.isSubmitting || updateOrganizationMutation.isLoading
aboutOrganizationFormMethods.formState.isSubmitting || updateOrganizationMutation.isPending
}
color="primary"
EndIcon={ArrowRight}
@@ -132,7 +132,7 @@ export const AddNewTeamsForm = () => {
<Button
StartIcon={Plus}
color="secondary"
disabled={createTeamsMutation.isLoading}
disabled={createTeamsMutation.isPending}
onClick={handleCounterIncrease}
aria-label={t("add_a_team")}
className="my-1">
@@ -143,7 +143,7 @@ export const AddNewTeamsForm = () => {
EndIcon={ArrowRight}
color="primary"
className="mt-6 w-full justify-center"
disabled={!formState.isValid || createTeamsMutation.isLoading || createTeamsMutation.isSuccess}
disabled={!formState.isValid || createTeamsMutation.isPending || createTeamsMutation.isSuccess}
onClick={handleFormSubmit}>
{t("continue")}
</Button>
@@ -80,7 +80,7 @@ export const CreateANewOrganizationForm = ({ slug }: { slug?: string }) => {
form={newOrganizationFormMethods}
id="createOrg"
handleSubmit={(v) => {
if (!createOrganizationMutation.isLoading) {
if (!createOrganizationMutation.isPending) {
setServerErrorMessage(null);
createOrganizationMutation.mutate(v);
}
@@ -188,7 +188,7 @@ export const CreateANewOrganizationForm = ({ slug }: { slug?: string }) => {
<div className="flex space-x-2 rtl:space-x-reverse">
<Button
disabled={
newOrganizationFormMethods.formState.isSubmitting || createOrganizationMutation.isLoading
newOrganizationFormMethods.formState.isSubmitting || createOrganizationMutation.isPending
}
color="primary"
EndIcon={ArrowRight}
@@ -61,7 +61,7 @@ export const SetPasswordForm = () => {
<Form
form={setPasswordFormMethods}
handleSubmit={(v) => {
if (!setPasswordMutation.isLoading) {
if (!setPasswordMutation.isPending) {
setServerErrorMessage(null);
setPasswordMutation.mutate({ newPassword: v.password });
}
@@ -97,7 +97,7 @@ export const SetPasswordForm = () => {
<div className="flex">
<Button
disabled={setPasswordFormMethods.formState.isSubmitting || setPasswordMutation.isLoading}
disabled={setPasswordFormMethods.formState.isSubmitting || setPasswordMutation.isPending}
color="primary"
EndIcon={ArrowRight}
type="submit"
@@ -44,7 +44,7 @@ export default function OtherTeamList(props: Props) {
key={team?.id as number}
team={team}
onActionSelect={(action: string) => selectAction(action, team?.id as number)}
isLoading={deleteTeamMutation.isLoading}
isPending={deleteTeamMutation.isPending}
hideDropdown={hideDropdown}
setHideDropdown={setHideDropdown}
/>
@@ -24,7 +24,7 @@ interface Props {
team: RouterOutputs["viewer"]["organizations"]["listOtherTeams"][number];
key: number;
onActionSelect: (text: string) => void;
isLoading?: boolean;
isPending?: boolean;
hideDropdown: boolean;
setHideDropdown: (value: boolean) => void;
}
@@ -140,7 +140,7 @@ export default function OtherTeamListItem(props: Props) {
variety="danger"
title={t("disband_team")}
confirmBtnText={t("confirm_disband_team")}
isLoading={props.isLoading}
isPending={props.isPending}
onConfirm={() => {
props.onActionSelect("disband");
}}>
@@ -1,5 +1,3 @@
import { useState } from "react";
import SkeletonLoaderTeamList from "@calcom/ee/teams/components/SkeletonloaderTeamList";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
@@ -10,21 +8,15 @@ import OtherTeamList from "./OtherTeamList";
export function OtherTeamsListing() {
const { t } = useLocale();
const [errorMessage, setErrorMessage] = useState("");
const { data: teams, isPending, error } = trpc.viewer.organizations.listOtherTeams.useQuery();
const { data: teams, isLoading } = trpc.viewer.organizations.listOtherTeams.useQuery(undefined, {
onError: (e) => {
setErrorMessage(e.message);
},
});
if (isLoading) {
if (isPending) {
return <SkeletonLoaderTeamList />;
}
return (
<>
{!!errorMessage && <Alert severity="error" title={errorMessage} />}
{!!error && <Alert severity="error" title={error.message} />}
{teams && teams.length > 0 ? (
<OtherTeamList teams={teams} />
@@ -98,7 +98,7 @@ const OrgForm = ({
required
{...form.register("metadata.orgAutoAcceptEmail")}
/>
<Button type="submit" color="primary" loading={mutation.isLoading}>
<Button type="submit" color="primary" loading={mutation.isPending}>
{t("save")}
</Button>
</Form>
@@ -1,7 +1,7 @@
"use client";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { useState, useEffect } from "react";
import { useForm } from "react-hook-form";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
@@ -107,7 +107,7 @@ const OrgAppearanceView = ({
calVideoLogo: newLogo,
});
}}
disabled={mutation.isLoading}
disabled={mutation.isPending}
imageSrc={currentOrg?.calVideoLogo ?? undefined}
uploadInstruction={t("cal_video_logo_upload_instruction")}
triggerButtonColor={currentOrg?.calVideoLogo ? "secondary" : "primary"}
@@ -115,7 +115,7 @@ const OrgAppearanceView = ({
{currentOrg?.calVideoLogo && (
<Button
color="destructive"
disabled={mutation.isLoading}
disabled={mutation.isPending}
onClick={() => {
mutation.mutate({
calVideoLogo: null,
@@ -190,7 +190,7 @@ const OrgAppearanceView = ({
<SettingsToggle
toggleSwitchAtTheEnd={true}
title={t("disable_cal_branding", { appName: APP_NAME })}
disabled={mutation?.isLoading}
disabled={mutation?.isPending}
description={t("removes_cal_branding", { appName: APP_NAME })}
checked={hideBrandingValue}
onCheckedChange={(checked) => {
@@ -212,13 +212,18 @@ const OrgAppearanceView = ({
const OrgAppearanceViewWrapper = () => {
const router = useRouter();
const { t } = useLocale();
const { data: currentOrg, isLoading } = trpc.viewer.organizations.listCurrent.useQuery(undefined, {
onError: () => {
router.push("/settings");
},
});
const { data: currentOrg, isPending, error } = trpc.viewer.organizations.listCurrent.useQuery();
if (isLoading) {
useEffect(
function refactorMeWithoutEffect() {
if (error) {
router.push("/settings");
}
},
[error]
);
if (isPending) {
return <AppearanceSkeletonLoader title={t("appearance")} description={t("appearance_org_description")} />;
}
@@ -1,6 +1,7 @@
"use client";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
@@ -50,14 +51,23 @@ const OrgGeneralView = () => {
const { t } = useLocale();
const router = useRouter();
const { data: currentOrg, isLoading } = trpc.viewer.organizations.listCurrent.useQuery(undefined, {
onError: () => {
router.push("/settings");
},
});
const {
data: currentOrg,
isPending,
error,
} = trpc.viewer.organizations.listCurrent.useQuery(undefined, {});
const { data: user } = trpc.viewer.me.useQuery();
if (isLoading) return <SkeletonLoader title={t("general")} description={t("general_description")} />;
useEffect(
function refactorMeWithoutEffect() {
if (error) {
router.push("/settings");
}
},
[error]
);
if (isPending) return <SkeletonLoader title={t("general")} description={t("general_description")} />;
if (!currentOrg) {
return null;
}
@@ -1,9 +1,10 @@
"use client";
// import { debounce } from "lodash";
import { keepPreviousData } from "@tanstack/react-query";
import { useSession } from "next-auth/react";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { useState, useEffect } from "react";
import MemberInvitationModal from "@calcom/ee/teams/components/MemberInvitationModal";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -71,20 +72,21 @@ const MembersView = () => {
// const [queryToFetch, setQueryToFetch] = useState<string | undefined>("");
const limit = 20;
const [showMemberInvitationModal, setShowMemberInvitationModal] = useState<boolean>(false);
const [members, setMembers] = useState<Members>([]);
const { data: currentOrg } = trpc.viewer.organizations.listCurrent.useQuery(undefined, {
enabled: !!session.data?.user?.org,
});
const { data: team, isLoading: isTeamLoading } = trpc.viewer.organizations.getOtherTeam.useQuery(
const {
data: team,
isPending: isTeamLoading,
error: otherTeamError,
} = trpc.viewer.organizations.getOtherTeam.useQuery(
{ teamId },
{
enabled: !Number.isNaN(teamId),
onError: () => {
router.push("/settings");
},
}
);
const { data: orgMembersNotInThisTeam, isLoading: isOrgListLoading } =
const { data: orgMembersNotInThisTeam, isPending: isOrgListLoading } =
trpc.viewer.organizations.getMembers.useQuery(
{
teamIdToExclude: teamId,
@@ -95,24 +97,40 @@ const MembersView = () => {
}
);
const { fetchNextPage, isFetchingNextPage, hasNextPage } =
trpc.viewer.organizations.listOtherTeamMembers.useInfiniteQuery(
{ teamId, limit },
{
onSuccess: (data) => {
const flatData = data?.pages?.flatMap((page) => page.rows) as Members;
setMembers(flatData);
},
enabled: !Number.isNaN(teamId),
onError: () => {
router.push("/settings");
},
getNextPageParam: (lastPage) => lastPage.nextCursor,
keepPreviousData: true,
}
);
const {
fetchNextPage,
isFetchingNextPage,
hasNextPage,
error: otherMembersError,
data,
} = trpc.viewer.organizations.listOtherTeamMembers.useInfiniteQuery(
{ teamId, limit },
{
enabled: !Number.isNaN(teamId),
getNextPageParam: (lastPage) => lastPage.nextCursor,
placeholderData: keepPreviousData,
}
);
const isLoading = isTeamLoading || isOrgListLoading;
useEffect(
function refactorMeWithoutEffect() {
if (otherMembersError || otherTeamError) {
router.push("/settings");
}
},
[otherMembersError, otherTeamError]
);
useEffect(
function refactorMeWithoutEffect() {
if (data) {
router.push("/settings");
}
},
[data]
);
const isPending = isTeamLoading || isOrgListLoading;
const inviteMemberMutation = trpc.viewer.teams.inviteMember.useMutation({
onSuccess: () => {
utils.viewer.organizations.getMembers.invalidate();
@@ -147,7 +165,7 @@ const MembersView = () => {
)
}
/>
{!isLoading && (
{!isPending && (
<>
<div>
<>
@@ -163,7 +181,7 @@ const MembersView = () => {
placeholder={`${t("search")}...`}
/> */}
<MembersList
members={members}
members={data?.pages?.flatMap((page) => page.rows) ?? []}
team={team}
fetchNextPage={fetchNextPage}
hasNextPage={hasNextPage}
@@ -180,7 +198,7 @@ const MembersView = () => {
</div>
{showMemberInvitationModal && team && (
<MemberInvitationModal
isLoading={inviteMemberMutation.isLoading}
isPending={inviteMemberMutation.isPending}
isOpen={showMemberInvitationModal}
orgMembers={orgMembersNotInThisTeam}
teamId={team.id}
@@ -5,7 +5,7 @@ import type { Prisma } from "@prisma/client";
import { useSession } from "next-auth/react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState, useLayoutEffect } from "react";
import { useState, useLayoutEffect, useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import { z } from "zod";
@@ -18,7 +18,6 @@ import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
import objectKeys from "@calcom/lib/objectKeys";
import slugify from "@calcom/lib/slugify";
import turndown from "@calcom/lib/turndownService";
import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
import { SkeletonContainer, SkeletonText } from "@calcom/ui";
import {
@@ -81,26 +80,39 @@ const OtherTeamProfileView = () => {
});
const params = useParamsWithFallback();
const teamId = Number(params.id);
const { data: team, isLoading } = trpc.viewer.organizations.getOtherTeam.useQuery(
const {
data: team,
isPending,
error: teamError,
} = trpc.viewer.organizations.getOtherTeam.useQuery(
{ teamId: teamId },
{
enabled: !Number.isNaN(teamId),
onError: () => {
router.push("/settings");
},
onSuccess: (team: RouterOutputs["viewer"]["organizations"]["getOtherTeam"]) => {
if (team) {
form.setValue("name", team.name || "");
form.setValue("slug", team.slug || "");
form.setValue("logo", team.logo || "");
form.setValue("bio", team.bio || "");
if (team.slug === null && (team?.metadata as Prisma.JsonObject)?.requestedSlug) {
form.setValue("slug", ((team?.metadata as Prisma.JsonObject)?.requestedSlug as string) || "");
}
}
},
}
);
useEffect(
function refactorMeWithoutEffect() {
if (teamError) {
router.push("/settings");
}
},
[teamError]
);
useEffect(
function refactorMeWithoutEffect() {
if (team) {
form.setValue("name", team.name || "");
form.setValue("slug", team.slug || "");
form.setValue("logo", team.logo || "");
form.setValue("bio", team.bio || "");
if (team.slug === null && (team?.metadata as Prisma.JsonObject)?.requestedSlug) {
form.setValue("slug", ((team?.metadata as Prisma.JsonObject)?.requestedSlug as string) || "");
}
}
},
[team]
);
// This page can only be accessed by team admins (owner/admin)
const isAdmin = true;
@@ -157,7 +169,7 @@ const OtherTeamProfileView = () => {
return (
<>
<Meta title={t("profile")} description={t("profile_team_description")} />
{!isLoading ? (
{!isPending ? (
<>
{isAdmin ? (
<Form
@@ -249,7 +261,7 @@ const OtherTeamProfileView = () => {
/>
</div>
<p className="text-default mt-2 text-sm">{t("team_description")}</p>
<Button color="primary" className="mt-8" type="submit" loading={mutation.isLoading}>
<Button color="primary" className="mt-8" type="submit" loading={mutation.isPending}>
{t("update")}
</Button>
{IS_TEAM_BILLING_ENABLED &&
@@ -4,7 +4,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
import type { Prisma } from "@prisma/client";
import { LinkIcon } from "lucide-react";
import { useRouter } from "next/navigation";
import { useState, useLayoutEffect } from "react";
import { useState, useLayoutEffect, useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import { z } from "zod";
@@ -79,13 +79,22 @@ const OrgProfileView = () => {
document.body.focus();
}, []);
const { data: currentOrganisation, isLoading } = trpc.viewer.organizations.listCurrent.useQuery(undefined, {
onError: () => {
router.push("/settings");
},
});
const {
data: currentOrganisation,
isPending,
error,
} = trpc.viewer.organizations.listCurrent.useQuery(undefined, {});
if (isLoading || !orgBranding || !currentOrganisation) {
useEffect(
function refactorMeWithoutEffect() {
if (error) {
router.push("/settings");
}
},
[error]
);
if (isPending || !orgBranding || !currentOrganisation) {
return <SkeletonLoader title={t("profile")} description={t("profile_org_description")} />;
}
@@ -286,7 +295,7 @@ const OrgProfileForm = ({ defaultValues }: { defaultValues: FormValues }) => {
<p className="text-default mt-2 text-sm">{t("org_description")}</p>
</div>
<SectionBottomActions align="end">
<Button color="primary" type="submit" loading={mutation.isLoading} disabled={isDisabled}>
<Button color="primary" type="submit" loading={mutation.isPending} disabled={isDisabled}>
{t("update")}
</Button>
</SectionBottomActions>
@@ -1,5 +1,3 @@
import { useState } from "react";
import ConnectionInfo from "@calcom/ee/sso/components/ConnectionInfo";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
import OIDCConnection from "@calcom/features/ee/sso/components/OIDCConnection";
@@ -22,23 +20,15 @@ const SkeletonLoader = ({ title, description }: { title: string; description: st
};
export default function SSOConfiguration({ teamId }: { teamId: number | null }) {
const [errorMessage, setErrorMessage] = useState("");
const { t } = useLocale();
const { data: connection, isLoading } = trpc.viewer.saml.get.useQuery(
{ teamId },
{
onError: (err) => {
setErrorMessage(err.message);
},
}
);
const { data: connection, isPending, error } = trpc.viewer.saml.get.useQuery({ teamId });
if (isLoading) {
if (isPending) {
return <SkeletonLoader title={t("sso_configuration")} description={t("sso_configuration_description")} />;
}
if (errorMessage) {
if (error) {
return (
<>
<Meta
@@ -46,7 +36,7 @@ export default function SSOConfiguration({ teamId }: { teamId: number | null })
description={t("sso_configuration_description")}
borderInShellHeader={true}
/>
<Alert severity="warning" message={t(errorMessage)} className="mt-4" />
<Alert severity="warning" message={t(error.message)} className="mt-4" />
</>
);
}
@@ -19,14 +19,7 @@ const SAMLSSO = () => {
const teamId = Number(params.id);
const { data: team, isLoading } = trpc.viewer.teams.get.useQuery(
{ teamId },
{
onError: () => {
router.push("/settings");
},
}
);
const { data: team, isPending, error } = trpc.viewer.teams.get.useQuery({ teamId });
useEffect(() => {
if (!HOSTED_CAL_FEATURES) {
@@ -34,7 +27,15 @@ const SAMLSSO = () => {
}
}, []);
if (isLoading) {
useEffect(
function refactorMeWithoutEffect() {
if (error) {
router.push("/settings");
}
},
[error]
);
if (isPending) {
return <SkeletonLoader />;
}
@@ -166,7 +166,7 @@ export default function HelpMenuItem({ onHelpItemSelect }: HelpMenuItemProps) {
<div className="my-2 flex justify-end">
<Button
disabled={disableSubmit}
loading={mutation.isLoading}
loading={mutation.isPending}
onClick={async () => {
if (rating && comment) {
await sendFeedback(rating, comment);
@@ -79,7 +79,7 @@ export const AddNewTeamMembersForm = ({
const [memberInviteModal, setMemberInviteModal] = useState(showDialog);
const [inviteLinkSettingsModal, setInviteLinkSettingsModal] = useState(false);
const { data: team, isLoading } = trpc.viewer.teams.get.useQuery({ teamId, isOrg }, { enabled: !!teamId });
const { data: team, isPending } = trpc.viewer.teams.get.useQuery({ teamId, isOrg }, { enabled: !!teamId });
const { data: orgMembersNotInThisTeam } = trpc.viewer.organizations.getMembers.useQuery(
{
teamIdToExclude: teamId,
@@ -126,12 +126,12 @@ export const AddNewTeamMembersForm = ({
{isOrg ? t("add_org_members") : t("add_team_member")}
</Button>
</div>
{isLoading ? (
{isPending ? (
<SkeletonButton />
) : (
<>
<MemberInvitationModal
isLoading={inviteMemberMutation.isLoading}
isPending={inviteMemberMutation.isPending}
isOpen={memberInviteModal}
orgMembers={orgMembersNotInThisTeam}
teamId={teamId}
@@ -199,7 +199,7 @@ export const AddNewTeamMembersForm = ({
EndIcon={!orgBranding || isOrg ? ArrowRight : undefined}
color="primary"
className="w-full justify-center"
disabled={publishTeamMutation.isLoading}
disabled={publishTeamMutation.isPending}
onClick={() => {
let uri = `/settings/teams/${teamId}/profile`;
if (isOrg) {
@@ -71,7 +71,7 @@ export const CreateANewTeamForm = () => {
<Form
form={newTeamFormMethods}
handleSubmit={(v) => {
if (!createTeamMutation.isLoading) {
if (!createTeamMutation.isPending) {
setServerErrorMessage(null);
createTeamMutation.mutate(v);
}
@@ -95,7 +95,7 @@ export const CreateANewTeamForm = () => {
<TextField
disabled={
/* E2e is too fast and it tries to fill this way before the form is ready */
!isLocaleReady || createTeamMutation.isLoading
!isLocaleReady || createTeamMutation.isPending
}
className="mt-2"
placeholder="Acme Inc."
@@ -146,14 +146,14 @@ export const CreateANewTeamForm = () => {
<div className="flex space-x-2 rtl:space-x-reverse">
<Button
disabled={createTeamMutation.isLoading}
disabled={createTeamMutation.isPending}
color="secondary"
href={returnToParam}
className="w-full justify-center">
{t("cancel")}
</Button>
<Button
disabled={newTeamFormMethods.formState.isSubmitting || createTeamMutation.isLoading}
disabled={newTeamFormMethods.formState.isSubmitting || createTeamMutation.isPending}
color="primary"
EndIcon={ArrowRight}
type="submit"
@@ -26,14 +26,14 @@ const DisableTeamImpersonation = ({
},
});
const [allowImpersonation, setAllowImpersonation] = useState(!query.data?.disableImpersonation ?? true);
if (query.isLoading) return <></>;
if (query.isPending) return <></>;
return (
<>
<SettingsToggle
toggleSwitchAtTheEnd={true}
title={t("user_impersonation_heading")}
disabled={disabled || mutation?.isLoading}
disabled={disabled || mutation?.isPending}
description={t("team_impersonation_description")}
checked={allowImpersonation}
onCheckedChange={(_allowImpersonation) => {
@@ -76,14 +76,14 @@ export function GoogleWorkspaceInviteButton(
}}
className="w-full justify-center gap-2"
StartIcon={UsersIcon}
loading={mutation.isLoading}>
loading={mutation.isPending}>
{t("import_from_google_workspace")}
</Button>
</Tooltip>
<Tooltip content="Remove workspace connection">
<Button
color="secondary"
loading={removeConnectionMutation.isLoading}
loading={removeConnectionMutation.isPending}
StartIcon={XIcon}
onClick={() => {
removeConnectionMutation.mutate();
@@ -34,7 +34,7 @@ const MakeTeamPrivateSwitch = ({
<SettingsToggle
toggleSwitchAtTheEnd={true}
title={t("make_team_private")}
disabled={disabled || mutation?.isLoading}
disabled={disabled || mutation?.isPending}
description={t("make_team_private_description")}
checked={isTeamPrivate}
onCheckedChange={(checked) => {

Some files were not shown because too many files have changed in this diff Show More