Merge branch 'main' into pr/6775

This commit is contained in:
zomars 2023-02-10 14:07:59 -07:00
commit 17c374e7d4
22 changed files with 223 additions and 176 deletions

View File

@ -5,6 +5,7 @@ import { FormEvent, useRef, useState } from "react";
import { useForm } from "react-hook-form";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import turndown from "@calcom/lib/turndownService";
import { trpc } from "@calcom/trpc/react";
import { Button, Editor, ImageUploader, Label, showToast } from "@calcom/ui";
@ -34,6 +35,7 @@ const UserProfile = (props: IUserProfileProps) => {
const utils = trpc.useContext();
const router = useRouter();
const createEventType = trpc.viewer.eventTypes.create.useMutation();
const telemetry = useTelemetry();
const mutation = trpc.viewer.updateProfile.useMutation({
onSuccess: async (_data, context) => {
@ -64,6 +66,8 @@ const UserProfile = (props: IUserProfileProps) => {
const onSubmit = handleSubmit((data: { bio: string }) => {
const { bio } = data;
telemetry.event(telemetryEventTypes.onboardingFinished);
mutation.mutate({
bio,
completedOnboarding: true,

View File

@ -1,9 +1,10 @@
import { ArrowRightIcon } from "@heroicons/react/outline";
import { useState } from "react";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import dayjs from "@calcom/dayjs";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { trpc } from "@calcom/trpc/react";
import { Button, TimezoneSelect } from "@calcom/ui";
@ -20,7 +21,7 @@ const UserSettings = (props: IUserSettingsProps) => {
const { user, nextStep } = props;
const { t } = useLocale();
const [selectedTimeZone, setSelectedTimeZone] = useState(dayjs.tz.guess());
const telemetry = useTelemetry();
const {
register,
handleSubmit,
@ -31,6 +32,11 @@ const UserSettings = (props: IUserSettingsProps) => {
},
reValidateMode: "onChange",
});
useEffect(() => {
telemetry.event(telemetryEventTypes.onboardingStarted);
}, [telemetry]);
const defaultOptions = { required: true, maxLength: 255 };
const utils = trpc.useContext();

View File

@ -1,4 +1,3 @@
import { useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { IS_SELF_HOSTED } from "@calcom/lib/constants";
@ -6,6 +5,8 @@ import { User } from "@calcom/prisma/client";
import { TRPCClientErrorLike } from "@calcom/trpc/client";
import { AppRouter } from "@calcom/trpc/server/routers/_app";
import useRouterQuery from "@lib/hooks/useRouterQuery";
import { PremiumTextfield } from "./PremiumTextfield";
import { UsernameTextfield } from "./UsernameTextfield";
@ -21,7 +22,7 @@ export const UsernameAvailabilityField = ({
onErrorMutation,
user,
}: UsernameAvailabilityFieldProps) => {
const [currentUsername, setCurrentUsername] = useState(user.username ?? "");
const { username: currentUsername, setQuery: setCurrentUsername } = useRouterQuery("username");
const formMethods = useForm({
defaultValues: {
username: currentUsername,

View File

@ -88,7 +88,7 @@ const withQuery = <
TInput = inferProcedureInput<TQuery>,
TOutput = inferProcedureOutput<TQuery>
>(
queryProcedure: DecorateProcedure<TQuery, any>,
queryProcedure: DecorateProcedure<TQuery, any, any>,
input?: TInput,
params?: UseTRPCQueryOptions<any, TInput, TOutput, TOutput, TError>
) => {

View File

@ -1,18 +1,10 @@
import { DefaultSeo } from "next-seo";
import Head from "next/head";
import Script from "next/script";
import superjson from "superjson";
import "@calcom/embed-core/src/embed-iframe";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
import { httpBatchLink } from "@calcom/trpc/client/links/httpBatchLink";
import { httpLink } from "@calcom/trpc/client/links/httpLink";
import { loggerLink } from "@calcom/trpc/client/links/loggerLink";
import { splitLink } from "@calcom/trpc/client/links/splitLink";
import { withTRPC } from "@calcom/trpc/next";
import type { TRPCClientErrorLike } from "@calcom/trpc/react";
import { Maybe } from "@calcom/trpc/server";
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
import { trpc } from "@calcom/trpc/react";
import AppProviders, { AppProps } from "@lib/app-providers";
import { seoConfig } from "@lib/config/next-seo.config";
@ -70,79 +62,4 @@ function MyApp(props: AppProps) {
);
}
export default withTRPC<AppRouter>({
config() {
const url =
typeof window !== "undefined"
? "/api/trpc"
: process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}/api/trpc`
: `http://${process.env.NEXT_PUBLIC_WEBAPP_URL}/api/trpc`;
/**
* If you want to use SSR, you need to use the server's full URL
* @link https://trpc.io/docs/ssr
*/
return {
/**
* @link https://trpc.io/docs/links
*/
links: [
// adds pretty logs to your console in development and logs errors in production
loggerLink({
enabled: (opts) =>
!!process.env.NEXT_PUBLIC_DEBUG || (opts.direction === "down" && opts.result instanceof Error),
}),
splitLink({
// check for context property `skipBatch`
condition: (op) => {
return op.context.skipBatch === true;
},
// when condition is true, use normal request
true: httpLink({ url }),
// when condition is false, use batching
false: httpBatchLink({
url,
/** @link https://github.com/trpc/trpc/issues/2008 */
// maxBatchSize: 7
}),
}),
],
/**
* @link https://react-query.tanstack.com/reference/QueryClient
*/
queryClientConfig: {
defaultOptions: {
queries: {
/**
* 1s should be enough to just keep identical query waterfalls low
* @example if one page components uses a query that is also used further down the tree
*/
staleTime: 1000,
/**
* Retry `useQuery()` calls depending on this function
*/
retry(failureCount, _err) {
const err = _err as never as Maybe<TRPCClientErrorLike<AppRouter>>;
const code = err?.data?.code;
if (code === "BAD_REQUEST" || code === "FORBIDDEN" || code === "UNAUTHORIZED") {
// if input data is wrong or you're not authorized there's no point retrying a query
return false;
}
const MAX_QUERY_RETRIES = 3;
return failureCount < MAX_QUERY_RETRIES;
},
},
},
},
/**
* @link https://trpc.io/docs/data-transformers
*/
transformer: superjson,
};
},
/**
* @link https://trpc.io/docs/ssr
*/
ssr: false,
})(MyApp);
export default trpc.withTRPC(MyApp);

View File

@ -182,8 +182,6 @@ export default function Login({
StartIcon={FaGoogle}
onClick={async (e) => {
e.preventDefault();
// track Google logins. Without personal data/payload
telemetry.event(telemetryEventTypes.googleLogin, collectPageParameters());
await signIn("google");
}}>
{t("signin_with_google")}

View File

@ -100,9 +100,11 @@ export default function Setup(props: inferSSRProps<typeof getServerSideProps>) {
const currentStep = isFreeLicense ? 3 : 4;
return (
<AdminAppsList
id={`wizard-step-${currentStep}`}
name={`wizard-step-${currentStep}`}
classNames={{
form: "mb-4 rounded-md bg-white px-0 pt-0 md:max-w-full",
appCategoryNavigationContainer: " max-h-[400px] overflow-y-auto md:p-4",
appCategoryNavigationContainer: "max-h-[400px] overflow-y-auto md:p-4",
verticalTabsItem: "!w-48 md:p-4",
}}
baseURL={`/auth/setup?step=${currentStep}`}
@ -127,7 +129,6 @@ export default function Setup(props: inferSSRProps<typeof getServerSideProps>) {
finishLabel={t("finish")}
prevLabel={t("prev_step")}
stepLabel={(currentStep, maxSteps) => t("current_step_of_total", { currentStep, maxSteps })}
containerClassname="md:w-[800px]"
/>
</main>
</>

View File

@ -1,7 +1,5 @@
import dayjs from "@calcom/dayjs";
import { Calendar } from "@calcom/features/calendars/weeklyview";
import Shell from "@calcom/features/shell/Shell";
import { yyyymmdd } from "@calcom/lib/date-fns";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { RouterOutputs, trpc } from "@calcom/trpc/react";
import { SkeletonText } from "@calcom/ui";
@ -18,13 +16,13 @@ export interface IBusySlot {
}
const AvailabilityView = ({ user }: { user: User }) => {
const { t } = useLocale();
const { date, setQuery: setSelectedDate } = useRouterQuery("date");
const selectedDate = dayjs(date);
const formattedSelectedDate = selectedDate.format("YYYY-MM-DD");
const { data } = trpc.viewer.availability.user.useQuery(
const { data, isLoading } = trpc.viewer.availability.user.useQuery(
{
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
username: user.username!,
dateFrom: selectedDate.startOf("day").utc().format(),
dateTo: selectedDate.endOf("day").utc().format(),
@ -47,26 +45,70 @@ const AvailabilityView = ({ user }: { user: User }) => {
}, [] as IBusySlot[]) || [];
return (
<div className="max-w-xl overflow-hidden rounded-md border border-gray-200 bg-white">
<div className="px-4">
<Calendar
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
events={[...(data?.busy || []), ...overrides]
.sort((a: IBusySlot, b: IBusySlot) => (a.start > b.start ? -1 : 1))
.map((slot) => ({
// TODO: Modify the Calendar view to be better at displaying blocks.
id: undefined,
title:
(slot.title ? `(${slot.title}) - ` : "") + (slot.source ? `(source: ${slot.source})` : ""),
start: new Date(slot.start),
end: new Date(slot.end),
}))}
onDateChange={(e) => setSelectedDate(yyyymmdd(e))}
startDate={selectedDate.startOf("day").toDate()}
endDate={selectedDate.endOf("day").toDate()}
view="day"
<div className="max-w-xl overflow-hidden rounded-md bg-white shadow">
<div className="px-4 py-5 sm:p-6">
{t("overview_of_day")}{" "}
<input
type="date"
className="inline h-8 border-none p-0"
defaultValue={formattedSelectedDate}
onChange={(e) => {
if (e.target.value) setSelectedDate(e.target.value);
}}
/>
<small className="block text-gray-400">{t("hover_over_bold_times_tip")}</small>
<div className="mt-4 space-y-4">
<div className="bg-brand dark:bg-darkmodebrand overflow-hidden rounded-md">
<div className="text-brandcontrast dark:text-darkmodebrandcontrast px-4 py-2 sm:px-6">
{t("your_day_starts_at")} {convertMinsToHrsMins(user.startTime)}
</div>
</div>
{(() => {
if (isLoading)
return (
<>
<SkeletonText className="block h-16 w-full" />
<SkeletonText className="block h-16 w-full" />
</>
);
if (data && (data.busy.length > 0 || overrides.length > 0))
return [...data.busy, ...overrides]
.sort((a: IBusySlot, b: IBusySlot) => (a.start > b.start ? -1 : 1))
.map((slot: IBusySlot) => (
<div
key={dayjs(slot.start).format("HH:mm")}
className="overflow-hidden rounded-md bg-gray-100"
data-testid="troubleshooter-busy-time">
<div className="px-4 py-5 text-black sm:p-6">
{t("calendar_shows_busy_between")}{" "}
<span className="font-medium text-gray-800" title={dayjs(slot.start).format("HH:mm")}>
{dayjs(slot.start).format("HH:mm")}
</span>{" "}
{t("and")}{" "}
<span className="font-medium text-gray-800" title={dayjs(slot.end).format("HH:mm")}>
{dayjs(slot.end).format("HH:mm")}
</span>{" "}
{t("on")} {dayjs(slot.start).format("D")}{" "}
{t(dayjs(slot.start).format("MMMM").toLowerCase())} {dayjs(slot.start).format("YYYY")}
{slot.title && ` - (${slot.title})`}
{slot.source && <small>{` - (source: ${slot.source})`}</small>}
</div>
</div>
));
return (
<div className="overflow-hidden rounded-md bg-gray-100">
<div className="px-4 py-5 text-black sm:p-6">{t("calendar_no_busy_slots")}</div>
</div>
);
})()}
<div className="bg-brand dark:bg-darkmodebrand overflow-hidden rounded-md">
<div className="text-brandcontrast dark:text-darkmodebrandcontrast px-4 py-2 sm:px-6">
{t("your_day_ends_at")} {convertMinsToHrsMins(user.endTime)}
</div>
</div>
</div>
</div>
</div>
);
@ -83,3 +125,11 @@ export default function Troubleshoot() {
</div>
);
}
function convertMinsToHrsMins(mins: number) {
const h = Math.floor(mins / 60);
const m = mins % 60;
const hs = h < 10 ? "0" + h : h;
const ms = m < 10 ? "0" + m : m;
return `${hs}:${ms}`;
}

View File

@ -386,15 +386,13 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
/>
</Tooltip>
<Dropdown modal={false}>
<DropdownMenuTrigger
asChild
data-testid={"event-type-options-" + type.id}
className="radix-state-open:rounded-r-md">
<DropdownMenuTrigger asChild data-testid={"event-type-options-" + type.id}>
<Button
type="button"
variant="icon"
color="secondary"
StartIcon={FiMoreHorizontal}
className="ltr:radix-state-open:rounded-r-md rtl:radix-state-open:rounded-l-md"
/>
</DropdownMenuTrigger>
<DropdownMenuContent>

View File

@ -56,7 +56,7 @@ export default function Signup({ prepopulateFormValues }: inferSSRProps<typeof g
})
.then(handleErrors)
.then(async () => {
telemetry.event(telemetryEventTypes.login, collectPageParameters());
telemetry.event(telemetryEventTypes.signup, collectPageParameters());
await signIn<"credentials">("credentials", {
...data,
callbackUrl: router.query.callbackUrl

View File

@ -32,16 +32,15 @@ test.describe("Availablity tests", () => {
await page.locator('[form="availability-form"][type="submit"]').click();
});
// The troubleshooter is unaware of the selected schedule, kept the override logic for now but needs rework.
// await test.step("Date override is displayed in troubleshooter", async () => {
// const response = await page.waitForResponse("**/api/trpc/viewer.availability.schedule.update?batch=1");
// const json = await response.json();
// // @ts-expect-error trust me bro
// const date = json[0].result.data.json.schedule.availability.find((a) => !!a.date);
// const troubleshooterURL = `/availability/troubleshoot?date=${dayjs(date.date).format("YYYY-MM-DD")}`;
// await page.goto(troubleshooterURL);
// await expect(page.locator('[data-testid="troubleshooter-busy-time"]')).toHaveCount(1);
// });
await test.step("Date override is displayed in troubleshooter", async () => {
const response = await page.waitForResponse("**/api/trpc/viewer.availability.schedule.update?batch=1");
const json = await response.json();
// @ts-expect-error trust me bro
const date = json[0].result.data.json.schedule.availability.find((a) => !!a.date);
const troubleshooterURL = `/availability/troubleshoot?date=${dayjs(date.date).format("YYYY-MM-DD")}`;
await page.goto(troubleshooterURL);
await expect(page.locator('[data-testid="troubleshooter-busy-time"]')).toHaveCount(1);
});
});
test("Availablity pages", async ({ page }) => {

View File

@ -5,7 +5,6 @@ import z from "zod";
import { HOSTED_CAL_FEATURES } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { trpc } from "@calcom/trpc/react";
import { Button } from "@calcom/ui";
import { FiLock } from "@calcom/ui/components/icon";
@ -23,7 +22,6 @@ const schema = z.object({
export function SAMLLogin({ samlTenantID, samlProductID, setErrorMessage }: Props) {
const { t } = useLocale();
const methods = useFormContext();
const telemetry = useTelemetry();
const mutation = trpc.viewer.public.samlTenantProduct.useMutation({
onSuccess: async (data) => {
@ -43,9 +41,6 @@ export function SAMLLogin({ samlTenantID, samlProductID, setErrorMessage }: Prop
onClick={async (event) => {
event.preventDefault();
// track Google logins. Without personal data/payload
telemetry.event(telemetryEventTypes.googleLogin, collectPageParameters());
if (!HOSTED_CAL_FEATURES) {
await signIn("saml", {}, { tenant: samlTenantID, product: samlProductID });
return;

View File

@ -14,8 +14,7 @@ export function SchedulerHeading() {
return (
<header className="flex flex-none flex-col justify-between py-4 sm:flex-row sm:items-center">
<h1 className="text-xl font-semibold text-gray-900">
{startDate.format("MMM DD")}
{!startDate.isSame(endDate, "day") ? `-${endDate.format("DD")}` : ""}
{startDate.format("MMM DD")}-{endDate.format("DD")}
<span className="text-gray-500">,{startDate.format("YYYY")}</span>
</h1>
<div className="flex items-center space-x-2 rtl:space-x-reverse">

View File

@ -3,7 +3,6 @@ import { Controller, useForm } from "react-hook-form";
import type { SSOConnection } from "@calcom/ee/sso/lib/saml";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { trpc } from "@calcom/trpc/react";
import { Button, DialogFooter, Form, showToast, TextField, Dialog, DialogContent } from "@calcom/ui";
@ -56,12 +55,10 @@ const CreateConnectionDialog = ({
}) => {
const { t } = useLocale();
const utils = trpc.useContext();
const telemetry = useTelemetry();
const form = useForm<FormValues>();
const mutation = trpc.viewer.saml.updateOIDC.useMutation({
async onSuccess() {
telemetry.event(telemetryEventTypes.samlConfig, collectPageParameters());
showToast(
t("sso_connection_created_successfully", {
connectionType: "OIDC",

View File

@ -3,7 +3,6 @@ import { Controller, useForm } from "react-hook-form";
import type { SSOConnection } from "@calcom/ee/sso/lib/saml";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { trpc } from "@calcom/trpc/react";
import { Button, DialogFooter, Form, showToast, TextArea, Dialog, DialogContent } from "@calcom/ui";
@ -54,12 +53,10 @@ const CreateConnectionDialog = ({
}) => {
const { t } = useLocale();
const utils = trpc.useContext();
const telemetry = useTelemetry();
const form = useForm<FormValues>();
const mutation = trpc.viewer.saml.update.useMutation({
async onSuccess() {
telemetry.event(telemetryEventTypes.samlConfig, collectPageParameters());
showToast(
t("sso_connection_created_successfully", {
connectionType: "SAML",

View File

@ -5,6 +5,7 @@ import { z } from "zod";
import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import slugify from "@calcom/lib/slugify";
import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { trpc } from "@calcom/trpc/react";
import { Avatar, Button, Form, ImageUploader, TextField } from "@calcom/ui";
import { FiArrowRight } from "@calcom/ui/components/icon";
@ -18,7 +19,7 @@ const querySchema = z.object({
export const CreateANewTeamForm = () => {
const { t } = useLocale();
const router = useRouter();
const telemetry = useTelemetry();
const returnToParsed = querySchema.safeParse(router.query);
const returnToParam =
@ -29,6 +30,7 @@ export const CreateANewTeamForm = () => {
const createTeamMutation = trpc.viewer.teams.create.useMutation({
onSuccess: (data) => {
telemetry.event(telemetryEventTypes.team_created);
router.push(`/settings/teams/${data.id}/onboard-members`);
},
});

View File

@ -114,13 +114,13 @@ export const EventTypeDescription = ({ eventType, className }: EventTypeDescript
) : (
<></>
)}
{eventType.seatsPerTimeSlot && (
{eventType?.seatsPerTimeSlot ? (
<li>
<Badge variant="gray" size="lg" StartIcon={FiUser}>
<p>{t("event_type_seats", { numberOfSeats: eventType.seatsPerTimeSlot })} </p>
</Badge>
</li>
)}
) : null}
</ul>
</div>
</>

View File

@ -12,12 +12,16 @@ export const telemetryEventTypes = {
bookingConfirmed: "booking_confirmed",
bookingCancelled: "booking_cancelled",
importSubmitted: "import_submitted",
googleLogin: "google_login",
login: "login",
samlLogin: "saml_login",
samlConfig: "saml_config",
embedView: "embed_view",
embedBookingConfirmed: "embed_booking_confirmed",
onboardingFinished: "onboarding_finished",
onboardingStarted: "onboarding_started",
signup: "signup",
team_created: "team_created",
website: {
pageView: "website_page_view",
},
};
export function collectPageParameters(

View File

@ -7,10 +7,10 @@
"main": "index.ts",
"dependencies": {
"@tanstack/react-query": "^4.3.9",
"@trpc/client": "^10.0.0-rc.6",
"@trpc/next": "^10.0.0-rc.6",
"@trpc/react-query": "^10.0.0-rc.6",
"@trpc/server": "^10.0.0-rc.6",
"@trpc/client": "^10.10.0",
"@trpc/next": "^10.10.0",
"@trpc/react-query": "^10.10.0",
"@trpc/server": "^10.10.0",
"superjson": "1.9.1",
"zod": "^3.20.2"
}

View File

@ -1,17 +1,96 @@
import superjson from "superjson";
import { createTRPCReact } from "@trpc/react-query";
import { httpBatchLink } from "../client/links/httpBatchLink";
import { httpLink } from "../client/links/httpLink";
import { loggerLink } from "../client/links/loggerLink";
import { splitLink } from "../client/links/splitLink";
import { createTRPCNext } from "../next";
// Type-only import:
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export
import type { inferRouterInputs, inferRouterOutputs } from "../server";
import type { TRPCClientErrorLike } from "../react";
import type { inferRouterInputs, inferRouterOutputs, Maybe } from "../server";
import type { AppRouter } from "../server/routers/_app";
/**
* A set of strongly-typed React hooks from your `AppRouter` type signature with `createTRPCReact`.
* @link https://trpc.io/docs/v10/react#2-create-trpc-hooks
*/
export const trpc = createTRPCReact<AppRouter>();
export const trpc = createTRPCNext<AppRouter>({
config() {
const url =
typeof window !== "undefined"
? "/api/trpc"
: process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}/api/trpc`
: `http://${process.env.NEXT_PUBLIC_WEBAPP_URL}/api/trpc`;
/**
* If you want to use SSR, you need to use the server's full URL
* @link https://trpc.io/docs/ssr
*/
return {
/**
* @link https://trpc.io/docs/links
*/
links: [
// adds pretty logs to your console in development and logs errors in production
loggerLink({
enabled: (opts) =>
!!process.env.NEXT_PUBLIC_DEBUG || (opts.direction === "down" && opts.result instanceof Error),
}),
splitLink({
// check for context property `skipBatch`
condition: (op) => {
return op.context.skipBatch === true;
},
// when condition is true, use normal request
true: httpLink({ url }),
// when condition is false, use batching
false: httpBatchLink({
url,
/** @link https://github.com/trpc/trpc/issues/2008 */
// maxBatchSize: 7
}),
}),
],
/**
* @link https://react-query.tanstack.com/reference/QueryClient
*/
queryClientConfig: {
defaultOptions: {
queries: {
/**
* 1s should be enough to just keep identical query waterfalls low
* @example if one page components uses a query that is also used further down the tree
*/
staleTime: 1000,
/**
* Retry `useQuery()` calls depending on this function
*/
retry(failureCount, _err) {
const err = _err as never as Maybe<TRPCClientErrorLike<AppRouter>>;
const code = err?.data?.code;
if (code === "BAD_REQUEST" || code === "FORBIDDEN" || code === "UNAUTHORIZED") {
// if input data is wrong or you're not authorized there's no point retrying a query
return false;
}
const MAX_QUERY_RETRIES = 3;
return failureCount < MAX_QUERY_RETRIES;
},
},
},
},
/**
* @link https://trpc.io/docs/data-transformers
*/
transformer: superjson,
};
},
/**
* @link https://trpc.io/docs/ssr
*/
ssr: false,
});
export const transformer = superjson;

View File

@ -21,7 +21,7 @@ export function ButtonGroup({ children, combined = false, containerProps }: Prop
"flex",
!combined
? "space-x-2 rtl:space-x-reverse"
: "ltr:[&>*:first-child]:rounded-l-md ltr:[&>*:first-child]:border-l rtl:[&>*:first-child]:rounded-r-md rtl:[&>*:first-child]:border-r ltr:[&>*:last-child]:rounded-r-md rtl:[&>*:last-child]:rounded-l-md [&_button]:rounded-none [&_button]:ltr:border-l-0 rtl:[&_button]:border-r-0 [&_a]:rounded-none ltr:[&_a]:border-l-0 rtl:[&_a]:border-r-0",
: "ltr:[&>*:first-child]:ml-0 ltr:[&>*:first-child]:rounded-l-md ltr:[&>*:first-child]:border-l rtl:[&>*:first-child]:rounded-r-md rtl:[&>*:first-child]:border-r ltr:[&>*:last-child]:rounded-r-md rtl:[&>*:last-child]:rounded-l-md [&_button]:rounded-none [&_a]:rounded-none [&>a]:-ml-[1px] hover:[&>a]:z-[1] [&>button]:-ml-[1px] hover:[&>button]:z-[1]",
containerProps?.className
)}>
{children}

View File

@ -7835,27 +7835,27 @@
javascript-natural-sort "0.7.1"
lodash "4.17.21"
"@trpc/client@^10.0.0-rc.6":
version "10.0.0-rc.6"
resolved "https://registry.yarnpkg.com/@trpc/client/-/client-10.0.0-rc.6.tgz#de30c9107facdf7d54e000c1e6b95de50bbe7acd"
integrity sha512-/vyi0b9R+DOJgvYFFYSveRwIX4Ttu01Af0ORzZGsDHYZGB7lfSqHfQhizf+AHfEpQXQsJYy5ZFOt6RtvF6LuKg==
"@trpc/client@^10.10.0":
version "10.10.0"
resolved "https://registry.yarnpkg.com/@trpc/client/-/client-10.10.0.tgz#5abdbf399639f2fdfe605682d2cdf3f9dba87cb1"
integrity sha512-HRVGkOsR4FIYpyQILP84HLbj6pRnLKgxy4AIelTf9d9TxD60M5bNhbR2Uz3hqNSb9a2ppaRJBLv7twlV9b4qHQ==
"@trpc/next@^10.0.0-rc.6":
version "10.0.0-rc.6"
resolved "https://registry.yarnpkg.com/@trpc/next/-/next-10.0.0-rc.6.tgz#9c9a81183cc4c49ba0f21ee009595639448cacd3"
integrity sha512-YaQbctwvYYk1HlVcJedRyQGT1EP5QiKse+T7zNj4Be0ZAiNVJwedC4AQ9YQDh0nK6GXDoHGVk8ocSCkNvEBddw==
"@trpc/next@^10.10.0":
version "10.10.0"
resolved "https://registry.yarnpkg.com/@trpc/next/-/next-10.10.0.tgz#083ff327b68b005d60b19af07008377f830ad73a"
integrity sha512-7d84L2OoF0RW06drTbNGOOggwMes8JxI3Ln/VOIaYeERzwOFNCtWPmGjWCdq4l1SKbXC6+baS+b9n5cXc+euwA==
dependencies:
react-ssr-prepass "^1.5.0"
"@trpc/react-query@^10.0.0-rc.6":
version "10.0.0-rc.6"
resolved "https://registry.yarnpkg.com/@trpc/react-query/-/react-query-10.0.0-rc.6.tgz#a10cc0b4dc3a849d260d086e0dc3faccac7515dc"
integrity sha512-OHkoZLDz86Q7wez7ayLAZiWATePi+QkwavyNu/x6c0f/dC8n7CUIZAhAjUN4SuZYEeZxbW351QtPIQwODT2XVA==
"@trpc/react-query@^10.10.0":
version "10.10.0"
resolved "https://registry.yarnpkg.com/@trpc/react-query/-/react-query-10.10.0.tgz#1faf32056aa3ee5ecb2ffd0d15e8abbcd7eff911"
integrity sha512-Jc/uii1MPevf95/z/W3ufYGHvrFvrtkjxQ8UuXhJCzOgv/FGPqhmA5PH124nLHEgGLBA7zQxHumofhdXosEhUQ==
"@trpc/server@^10.0.0-rc.6":
version "10.0.0-rc.6"
resolved "https://registry.yarnpkg.com/@trpc/server/-/server-10.0.0-rc.6.tgz#076df5ab371ede9226868474e71333041020ec8a"
integrity sha512-8JAIX2SSTEwpdp+CRoPGsrj9A9qee0ltqa1fPXlKOtzQKbxDjGoqy6kVVh25YUJ8GJrXf4LhV8pZGkyZOOX6ow==
"@trpc/server@^10.10.0":
version "10.10.0"
resolved "https://registry.yarnpkg.com/@trpc/server/-/server-10.10.0.tgz#0b494335140d4fd5e1452f7b57dcc9b8886720a4"
integrity sha512-tCTqcqBT+3nebYFTHtwM877qo5xQPtVlptxKdUzMVWleWT4lFTL4oddk45qVURToci2iMbVJjd4jQU9y9/XwlQ==
"@tryvital/vital-node@^1.4.6":
version "1.4.6"