Ends the war between tRPC and next-i18next (#939)
* Ends the war between tRPC and next-i18next * Locale fixes * Linting * Linting * trpc i18n (not working) (#942) * simplify i18n handler and remove redundant(?) fn check * split up viewer to a "logged in only" and "public" * wip -- skip first render Co-authored-by: Omar López <zomars@me.com> * Linting * I18n fixes * We don't need serverSideTranslations in every page anymore Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: Alex Johansson <alexander@n1s.se>
This commit is contained in:
parent
26f20e2397
commit
0861d7cc61
28 changed files with 243 additions and 401 deletions
|
@ -9,44 +9,42 @@ export default function AddToHomescreen() {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
return (
|
||||
!closeBanner && (
|
||||
<div className="fixed sm:hidden bottom-0 inset-x-0 pb-2 sm:pb-5">
|
||||
<div className="max-w-7xl mx-auto px-2 sm:px-6 lg:px-8">
|
||||
<div className="p-2 rounded-lg shadow-lg sm:p-3" style={{ background: "#2F333D" }}>
|
||||
<div className="flex items-center justify-between flex-wrap">
|
||||
<div className="w-0 flex-1 flex items-center">
|
||||
<span className="flex p-2 rounded-lg bg-opacity-30 bg-black">
|
||||
<svg
|
||||
className="h-7 w-7 text-indigo-500 fill-current"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 50 50"
|
||||
enableBackground="new 0 0 50 50">
|
||||
<path d="M30.3 13.7L25 8.4l-5.3 5.3-1.4-1.4L25 5.6l6.7 6.7z" />
|
||||
<path d="M24 7h2v21h-2z" />
|
||||
<path d="M35 40H15c-1.7 0-3-1.3-3-3V19c0-1.7 1.3-3 3-3h7v2h-7c-.6 0-1 .4-1 1v18c0 .6.4 1 1 1h20c.6 0 1-.4 1-1V19c0-.6-.4-1-1-1h-7v-2h7c1.7 0 3 1.3 3 3v18c0 1.7-1.3 3-3 3z" />
|
||||
</svg>
|
||||
return !closeBanner ? (
|
||||
<div className="fixed sm:hidden bottom-0 inset-x-0 pb-2 sm:pb-5">
|
||||
<div className="max-w-7xl mx-auto px-2 sm:px-6 lg:px-8">
|
||||
<div className="p-2 rounded-lg shadow-lg sm:p-3" style={{ background: "#2F333D" }}>
|
||||
<div className="flex items-center justify-between flex-wrap">
|
||||
<div className="w-0 flex-1 flex items-center">
|
||||
<span className="flex p-2 rounded-lg bg-opacity-30 bg-black">
|
||||
<svg
|
||||
className="h-7 w-7 text-indigo-500 fill-current"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 50 50"
|
||||
enableBackground="new 0 0 50 50">
|
||||
<path d="M30.3 13.7L25 8.4l-5.3 5.3-1.4-1.4L25 5.6l6.7 6.7z" />
|
||||
<path d="M24 7h2v21h-2z" />
|
||||
<path d="M35 40H15c-1.7 0-3-1.3-3-3V19c0-1.7 1.3-3 3-3h7v2h-7c-.6 0-1 .4-1 1v18c0 .6.4 1 1 1h20c.6 0 1-.4 1-1V19c0-.6-.4-1-1-1h-7v-2h7c1.7 0 3 1.3 3 3v18c0 1.7-1.3 3-3 3z" />
|
||||
</svg>
|
||||
</span>
|
||||
<p className="ml-3 text-xs font-medium text-white">
|
||||
<span className="inline">
|
||||
Add this app to your home screen for faster access and improved experience.
|
||||
</span>
|
||||
<p className="ml-3 text-xs font-medium text-white">
|
||||
<span className="inline">
|
||||
Add this app to your home screen for faster access and improved experience.
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="order-2 flex-shrink-0 sm:order-3 sm:ml-2">
|
||||
<button
|
||||
onClick={() => setCloseBanner(true)}
|
||||
type="button"
|
||||
className="-mr-1 flex p-2 rounded-md hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-white">
|
||||
<span className="sr-only">Dismiss</span>
|
||||
<XIcon className="h-6 w-6 text-white" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="order-2 flex-shrink-0 sm:order-3 sm:ml-2">
|
||||
<button
|
||||
onClick={() => setCloseBanner(true)}
|
||||
type="button"
|
||||
className="-mr-1 flex p-2 rounded-md hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-white">
|
||||
<span className="sr-only">Dismiss</span>
|
||||
<XIcon className="h-6 w-6 text-white" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
</div>
|
||||
) : null;
|
||||
}
|
||||
|
|
|
@ -1,21 +1,18 @@
|
|||
import { useTranslation } from "next-i18next";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
interface Props {
|
||||
localeProp: string;
|
||||
}
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
const I18nLanguageHandler = ({ localeProp }: Props): null => {
|
||||
/**
|
||||
* Auto-switches locale client-side to the logged in user's preference
|
||||
*/
|
||||
const I18nLanguageHandler = (): null => {
|
||||
const { i18n } = useTranslation("common");
|
||||
const router = useRouter();
|
||||
const { pathname } = router;
|
||||
if (!localeProp)
|
||||
console.warn(
|
||||
`You may forgot to return 'localeProp' from 'getServerSideProps' or 'getStaticProps' in ${pathname}`
|
||||
);
|
||||
if (i18n.language !== localeProp) {
|
||||
i18n.changeLanguage(localeProp);
|
||||
const locale = trpc.useQuery(["viewer.i18n"]).data?.locale;
|
||||
|
||||
if (locale && i18n.language && i18n.language !== locale) {
|
||||
if (typeof i18n.changeLanguage === "function") i18n.changeLanguage(locale);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
import { signOut, useSession } from "next-auth/client";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { Fragment, ReactNode, useEffect } from "react";
|
||||
import React, { ReactNode, useEffect } from "react";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
import LicenseBanner from "@ee/components/LicenseBanner";
|
||||
|
@ -49,6 +49,7 @@ function useMeQuery() {
|
|||
function useRedirectToLoginIfUnauthenticated() {
|
||||
const [session, loading] = useSession();
|
||||
const router = useRouter();
|
||||
const query = useMeQuery();
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && !session) {
|
||||
|
@ -60,6 +61,27 @@ function useRedirectToLoginIfUnauthenticated() {
|
|||
});
|
||||
}
|
||||
}, [loading, session, router]);
|
||||
|
||||
if (query.status !== "loading" && !query.data) {
|
||||
router.replace("/auth/login");
|
||||
}
|
||||
}
|
||||
|
||||
function useRedirectToOnboardingIfNeeded() {
|
||||
const [session, loading] = useSession();
|
||||
const router = useRouter();
|
||||
const query = useMeQuery();
|
||||
const user = query.data;
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && user) {
|
||||
if (shouldShowOnboarding(user)) {
|
||||
router.replace({
|
||||
pathname: "/getting-started",
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [loading, session, router, user]);
|
||||
}
|
||||
|
||||
export function ShellSubHeading(props: {
|
||||
|
@ -90,20 +112,10 @@ export default function Shell(props: {
|
|||
CTA?: ReactNode;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
useRedirectToLoginIfUnauthenticated();
|
||||
useRedirectToOnboardingIfNeeded();
|
||||
|
||||
const telemetry = useTelemetry();
|
||||
const query = useMeQuery();
|
||||
|
||||
useEffect(
|
||||
function redirectToOnboardingIfNeeded() {
|
||||
if (query.data && shouldShowOnboarding(query.data)) {
|
||||
router.push("/getting-started");
|
||||
}
|
||||
},
|
||||
[query.data, router]
|
||||
);
|
||||
|
||||
const navigation = [
|
||||
{
|
||||
|
@ -142,11 +154,7 @@ export default function Shell(props: {
|
|||
telemetry.withJitsu((jitsu) => {
|
||||
return jitsu.track(telemetryEventTypes.pageView, collectPageParameters(router.asPath));
|
||||
});
|
||||
}, [telemetry]);
|
||||
|
||||
if (query.status !== "loading" && !query.data) {
|
||||
router.replace("/auth/login");
|
||||
}
|
||||
}, [telemetry, router.asPath]);
|
||||
|
||||
const pageTitle = typeof props.heading === "string" ? props.heading : props.title;
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { ArrowLeftIcon } from "@heroicons/react/solid";
|
||||
import { EventType } from "@prisma/client";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
import showToast from "@lib/notification";
|
||||
|
@ -8,11 +7,7 @@ import { Webhook } from "@lib/webhook";
|
|||
import Button from "@components/ui/Button";
|
||||
import Switch from "@components/ui/Switch";
|
||||
|
||||
export default function EditTeam(props: {
|
||||
webhook: Webhook;
|
||||
eventTypes: EventType[];
|
||||
onCloseEdit: () => void;
|
||||
}) {
|
||||
export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => void }) {
|
||||
const [bookingCreated, setBookingCreated] = useState(
|
||||
props.webhook.eventTriggers.includes("booking_created")
|
||||
);
|
||||
|
|
|
@ -1,18 +1,14 @@
|
|||
import { GetServerSidePropsContext } from "next";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
|
||||
import { PaymentData } from "@ee/lib/stripe/server";
|
||||
|
||||
import { asStringOrThrow } from "@lib/asStringOrNull";
|
||||
import { getOrSetUserLocaleFromHeaders } from "@lib/core/i18n/i18n.utils";
|
||||
import prisma from "@lib/prisma";
|
||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
|
||||
export type PaymentPageProps = inferSSRProps<typeof getServerSideProps>;
|
||||
|
||||
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
||||
const locale = await getOrSetUserLocaleFromHeaders(context.req);
|
||||
|
||||
const rawPayment = await prisma.payment.findFirst({
|
||||
where: {
|
||||
uid: asStringOrThrow(context.query.uid),
|
||||
|
@ -103,8 +99,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
booking,
|
||||
payment,
|
||||
profile,
|
||||
localeProp: locale,
|
||||
...(await serverSideTranslations(locale, ["common"])),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -66,7 +66,7 @@ export function QueryCell<TData, TError extends ErrorLike>(
|
|||
return opts.loading?.(query) ?? <Loader />;
|
||||
}
|
||||
if (query.status === "idle") {
|
||||
return null;
|
||||
return opts.idle?.(query) ?? <Loader />;
|
||||
}
|
||||
// impossible state
|
||||
return null;
|
||||
|
|
|
@ -1,18 +1,36 @@
|
|||
import { IdProvider } from "@radix-ui/react-id";
|
||||
import { Provider } from "next-auth/client";
|
||||
import { appWithTranslation } from "next-i18next";
|
||||
import { AppProps } from "next/dist/shared/lib/router/router";
|
||||
import React from "react";
|
||||
import React, { ComponentProps, ReactNode } from "react";
|
||||
|
||||
import DynamicIntercomProvider from "@ee/lib/intercom/providerDynamic";
|
||||
|
||||
import { createTelemetryClient, TelemetryProvider } from "@lib/telemetry";
|
||||
|
||||
import { trpc } from "./trpc";
|
||||
|
||||
const I18nextAdapter = appWithTranslation(({ children }: { children?: ReactNode }) => <>{children}</>);
|
||||
|
||||
const CustomI18nextProvider = (props: { children: ReactNode }) => {
|
||||
const { i18n, locale } = trpc.useQuery(["viewer.i18n"]).data ?? {};
|
||||
const passedProps = {
|
||||
...props,
|
||||
pageProps: { ...i18n },
|
||||
router: { locale },
|
||||
} as unknown as ComponentProps<typeof I18nextAdapter>;
|
||||
return <I18nextAdapter {...passedProps} />;
|
||||
};
|
||||
|
||||
const AppProviders = (props: AppProps) => {
|
||||
const session = trpc.useQuery(["viewer.session"]).data;
|
||||
return (
|
||||
<TelemetryProvider value={createTelemetryClient()}>
|
||||
<IdProvider>
|
||||
<DynamicIntercomProvider>
|
||||
<Provider session={props.pageProps.session}>{props.children}</Provider>
|
||||
<Provider session={session || undefined}>
|
||||
<CustomI18nextProvider>{props.children}</CustomI18nextProvider>
|
||||
</Provider>
|
||||
</DynamicIntercomProvider>
|
||||
</IdProvider>
|
||||
</TelemetryProvider>
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import { ArrowRightIcon } from "@heroicons/react/outline";
|
||||
import { GetServerSidePropsContext } from "next";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
import Link from "next/link";
|
||||
import React from "react";
|
||||
|
||||
import { getOrSetUserLocaleFromHeaders } from "@lib/core/i18n/i18n.utils";
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import useTheme from "@lib/hooks/useTheme";
|
||||
import prisma from "@lib/prisma";
|
||||
|
@ -75,7 +73,6 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
|
|||
|
||||
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
||||
const username = (context.query.user as string).toLowerCase();
|
||||
const locale = await getOrSetUserLocaleFromHeaders(context.req);
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
|
@ -139,10 +136,8 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
|
||||
return {
|
||||
props: {
|
||||
localeProp: locale,
|
||||
user,
|
||||
eventTypes,
|
||||
...(await serverSideTranslations(locale, ["common"])),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { Prisma } from "@prisma/client";
|
||||
import { GetServerSidePropsContext } from "next";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
|
||||
import { asStringOrNull } from "@lib/asStringOrNull";
|
||||
import { getOrSetUserLocaleFromHeaders } from "@lib/core/i18n/i18n.utils";
|
||||
import prisma from "@lib/prisma";
|
||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
|
||||
|
@ -16,7 +14,6 @@ export default function Type(props: AvailabilityPageProps) {
|
|||
}
|
||||
|
||||
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
||||
const locale = await getOrSetUserLocaleFromHeaders(context.req);
|
||||
// get query params and typecast them to string
|
||||
// (would be even better to assert them instead of typecasting)
|
||||
const userParam = asStringOrNull(context.query.user);
|
||||
|
@ -178,7 +175,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
|
||||
return {
|
||||
props: {
|
||||
localeProp: locale,
|
||||
profile: {
|
||||
name: user.name,
|
||||
image: user.avatar,
|
||||
|
@ -189,7 +185,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
date: dateParam,
|
||||
eventType: eventTypeObject,
|
||||
workingHours,
|
||||
...(await serverSideTranslations(locale, ["common"])),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -2,10 +2,8 @@ import dayjs from "dayjs";
|
|||
import timezone from "dayjs/plugin/timezone";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import { GetServerSidePropsContext } from "next";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
|
||||
import { asStringOrThrow } from "@lib/asStringOrNull";
|
||||
import { getOrSetUserLocaleFromHeaders } from "@lib/core/i18n/i18n.utils";
|
||||
import prisma from "@lib/prisma";
|
||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
|
||||
|
@ -21,8 +19,6 @@ export default function Book(props: BookPageProps) {
|
|||
}
|
||||
|
||||
export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
const locale = await getOrSetUserLocaleFromHeaders(context.req);
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
username: asStringOrThrow(context.query.user),
|
||||
|
@ -103,7 +99,6 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|||
|
||||
return {
|
||||
props: {
|
||||
localeProp: locale,
|
||||
profile: {
|
||||
slug: user.username,
|
||||
name: user.name,
|
||||
|
@ -112,7 +107,6 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|||
},
|
||||
eventType: eventTypeObject,
|
||||
booking,
|
||||
...(await serverSideTranslations(locale, ["common"])),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ import { loggerLink } from "@trpc/client/links/loggerLink";
|
|||
import { withTRPC } from "@trpc/next";
|
||||
import type { TRPCClientErrorLike } from "@trpc/react";
|
||||
import { Maybe } from "@trpc/server";
|
||||
import { appWithTranslation } from "next-i18next";
|
||||
import { DefaultSeo } from "next-seo";
|
||||
import type { AppProps as NextAppProps } from "next/app";
|
||||
import superjson from "superjson";
|
||||
|
@ -28,7 +27,7 @@ function MyApp(props: AppProps) {
|
|||
return (
|
||||
<AppProviders {...props}>
|
||||
<DefaultSeo {...seoConfig.defaultNextSeo} />
|
||||
<I18nLanguageHandler localeProp={pageProps.localeProp} />
|
||||
<I18nLanguageHandler />
|
||||
<Component {...pageProps} err={err} />
|
||||
</AppProviders>
|
||||
);
|
||||
|
@ -92,4 +91,4 @@ export default withTRPC<AppRouter>({
|
|||
* @link https://trpc.io/docs/ssr
|
||||
*/
|
||||
ssr: false,
|
||||
})(appWithTranslation(MyApp));
|
||||
})(MyApp);
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
import dayjs from "dayjs";
|
||||
import dayjs, { Dayjs } from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import { GetServerSidePropsContext } from "next";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { getSession } from "@lib/auth";
|
||||
import { getOrSetUserLocaleFromHeaders } from "@lib/core/i18n/i18n.utils";
|
||||
import { QueryCell } from "@lib/QueryCell";
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import prisma from "@lib/prisma";
|
||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
import { inferQueryOutput, trpc } from "@lib/trpc";
|
||||
|
||||
import Loader from "@components/Loader";
|
||||
import Shell from "@components/Shell";
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
export default function Troubleshoot({ user }: inferSSRProps<typeof getServerSideProps>) {
|
||||
type User = inferQueryOutput<"viewer.me">;
|
||||
|
||||
const AvailabilityView = ({ user }: { user: User }) => {
|
||||
const { t } = useLocale();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [availability, setAvailability] = useState([]);
|
||||
|
@ -29,108 +27,92 @@ export default function Troubleshoot({ user }: inferSSRProps<typeof getServerSid
|
|||
return `${h}:${m}`;
|
||||
}
|
||||
|
||||
const fetchAvailability = (date) => {
|
||||
const dateFrom = date.startOf("day").utc().format();
|
||||
const dateTo = date.endOf("day").utc().format();
|
||||
|
||||
fetch(`/api/availability/${user.username}?dateFrom=${dateFrom}&dateTo=${dateTo}`)
|
||||
.then((res) => {
|
||||
return res.json();
|
||||
})
|
||||
.then((availableIntervals) => {
|
||||
setAvailability(availableIntervals.busy);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchAvailability = (date: Dayjs) => {
|
||||
const dateFrom = date.startOf("day").utc().format();
|
||||
const dateTo = date.endOf("day").utc().format();
|
||||
setLoading(true);
|
||||
|
||||
fetch(`/api/availability/${user.username}?dateFrom=${dateFrom}&dateTo=${dateTo}`)
|
||||
.then((res) => {
|
||||
return res.json();
|
||||
})
|
||||
.then((availableIntervals) => {
|
||||
setAvailability(availableIntervals.busy);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
fetchAvailability(selectedDate);
|
||||
}, [selectedDate]);
|
||||
|
||||
if (loading) {
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Shell heading={t("troubleshoot")} subtitle={t("troubleshoot_description")}>
|
||||
<div className="bg-white max-w-xl overflow-hidden shadow rounded-sm">
|
||||
<div className="px-4 py-5 sm:p-6">
|
||||
{t("overview_of_day")}{" "}
|
||||
<input
|
||||
type="date"
|
||||
className="inline border-none h-8 p-0"
|
||||
defaultValue={selectedDate.format("YYYY-MM-DD")}
|
||||
onBlur={(e) => {
|
||||
setSelectedDate(dayjs(e.target.value));
|
||||
}}
|
||||
/>
|
||||
<small className="block text-neutral-400">{t("hover_over_bold_times_tip")}</small>
|
||||
<div className="mt-4 space-y-4">
|
||||
<div className="bg-black overflow-hidden rounded-sm">
|
||||
<div className="px-4 sm:px-6 py-2 text-white">
|
||||
{t("your_day_starts_at")} {convertMinsToHrsMins(user.startTime)}
|
||||
</div>
|
||||
</div>
|
||||
{availability.map((slot) => (
|
||||
<div key={slot.start} className="bg-neutral-100 overflow-hidden rounded-sm">
|
||||
<div className="px-4 py-5 sm:p-6 text-black">
|
||||
{t("calendar_shows_busy_between")}{" "}
|
||||
<span className="font-medium text-neutral-800" title={slot.start}>
|
||||
{dayjs(slot.start).format("HH:mm")}
|
||||
</span>{" "}
|
||||
{t("and")}{" "}
|
||||
<span className="font-medium text-neutral-800" title={slot.end}>
|
||||
{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")}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{availability.length === 0 && <Loader />}
|
||||
<div className="bg-black overflow-hidden rounded-sm">
|
||||
<div className="px-4 sm:px-6 py-2 text-white">
|
||||
{t("your_day_ends_at")} {convertMinsToHrsMins(user.endTime)}
|
||||
<div className="bg-white max-w-xl overflow-hidden shadow rounded-sm">
|
||||
<div className="px-4 py-5 sm:p-6">
|
||||
{t("overview_of_day")}{" "}
|
||||
<input
|
||||
type="date"
|
||||
className="inline border-none h-8 p-0"
|
||||
defaultValue={selectedDate.format("YYYY-MM-DD")}
|
||||
onChange={(e) => {
|
||||
setSelectedDate(dayjs(e.target.value));
|
||||
}}
|
||||
/>
|
||||
<small className="block text-neutral-400">{t("hover_over_bold_times_tip")}</small>
|
||||
<div className="mt-4 space-y-4">
|
||||
<div className="bg-black overflow-hidden rounded-sm">
|
||||
<div className="px-4 sm:px-6 py-2 text-white">
|
||||
{t("your_day_starts_at")} {convertMinsToHrsMins(user.startTime)}
|
||||
</div>
|
||||
</div>
|
||||
{loading ? (
|
||||
<Loader />
|
||||
) : availability.length > 0 ? (
|
||||
availability.map((slot) => (
|
||||
<div key={slot.start} className="bg-neutral-100 overflow-hidden rounded-sm">
|
||||
<div className="px-4 py-5 sm:p-6 text-black">
|
||||
{t("calendar_shows_busy_between")}{" "}
|
||||
<span className="font-medium text-neutral-800" title={slot.start}>
|
||||
{dayjs(slot.start).format("HH:mm")}
|
||||
</span>{" "}
|
||||
{t("and")}{" "}
|
||||
<span className="font-medium text-neutral-800" title={slot.end}>
|
||||
{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")}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="bg-neutral-100 overflow-hidden rounded-sm">
|
||||
<div className="px-4 py-5 sm:p-6 text-black">{t("calendar_no_busy_slots")}</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="bg-black overflow-hidden rounded-sm">
|
||||
<div className="px-4 sm:px-6 py-2 text-white">
|
||||
{t("your_day_ends_at")} {convertMinsToHrsMins(user.endTime)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function Troubleshoot() {
|
||||
const query = trpc.useQuery(["viewer.me"]);
|
||||
const { t } = useLocale();
|
||||
return (
|
||||
<div>
|
||||
<Shell heading={t("troubleshoot")} subtitle={t("troubleshoot_description")}>
|
||||
<QueryCell query={query} success={({ data }) => <AvailabilityView user={data} />} />
|
||||
</Shell>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
||||
const session = await getSession(context);
|
||||
const locale = await getOrSetUserLocaleFromHeaders(context.req);
|
||||
|
||||
if (!session?.user?.id) {
|
||||
return { redirect: { permanent: false, destination: "/auth/login" } };
|
||||
}
|
||||
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
id: session.user.id,
|
||||
},
|
||||
select: {
|
||||
startTime: true,
|
||||
endTime: true,
|
||||
username: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) return { redirect: { permanent: false, destination: "/auth/login" } };
|
||||
|
||||
return {
|
||||
props: {
|
||||
session,
|
||||
user,
|
||||
...(await serverSideTranslations(locale, ["common"])),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -23,7 +23,11 @@ export default function Bookings() {
|
|||
|
||||
const router = useRouter();
|
||||
const status = router.query?.status as BookingListingStatus;
|
||||
const query = trpc.useQuery(["viewer.bookings", { status }]);
|
||||
|
||||
const query = trpc.useQuery(["viewer.bookings", { status }], {
|
||||
// first render has status `undefined`
|
||||
enabled: !!status,
|
||||
});
|
||||
|
||||
return (
|
||||
<Shell heading={t("bookings")} subtitle={t("bookings_description")}>
|
||||
|
|
|
@ -2,11 +2,9 @@ import { CalendarIcon, XIcon } from "@heroicons/react/solid";
|
|||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import { getSession } from "next-auth/client";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
import { useRouter } from "next/router";
|
||||
import { useState } from "react";
|
||||
|
||||
import { getOrSetUserLocaleFromHeaders } from "@lib/core/i18n/i18n.utils";
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import prisma from "@lib/prisma";
|
||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry";
|
||||
|
@ -145,7 +143,6 @@ export default function Type(props) {
|
|||
|
||||
export async function getServerSideProps(context) {
|
||||
const session = await getSession(context);
|
||||
const locale = await getOrSetUserLocaleFromHeaders(context.req);
|
||||
const booking = await prisma.booking.findUnique({
|
||||
where: {
|
||||
uid: context.query.uid,
|
||||
|
@ -202,7 +199,6 @@ export async function getServerSideProps(context) {
|
|||
booking: bookingObj,
|
||||
cancellationAllowed:
|
||||
(!!session?.user && session.user.id == booking.user?.id) || booking.startTime >= new Date(),
|
||||
...(await serverSideTranslations(locale, ["common"])),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ import dayjs from "dayjs";
|
|||
import timezone from "dayjs/plugin/timezone";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import { GetServerSidePropsContext } from "next";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { FormattedNumber, IntlProvider } from "react-intl";
|
||||
|
@ -37,7 +36,6 @@ import {
|
|||
import { getSession } from "@lib/auth";
|
||||
import classNames from "@lib/classNames";
|
||||
import { HttpError } from "@lib/core/http/error";
|
||||
import { getOrSetUserLocaleFromHeaders } from "@lib/core/i18n/i18n.utils";
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import getIntegrations, { hasIntegration } from "@lib/integrations/getIntegrations";
|
||||
import { LocationType } from "@lib/location";
|
||||
|
@ -1097,8 +1095,6 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
||||
const { req, query } = context;
|
||||
const session = await getSession({ req });
|
||||
const locale = await getOrSetUserLocaleFromHeaders(context.req);
|
||||
|
||||
const typeParam = parseInt(asStringOrThrow(query.type));
|
||||
|
||||
if (!session?.user?.id) {
|
||||
|
@ -1278,7 +1274,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
return {
|
||||
props: {
|
||||
session,
|
||||
localeProp: locale,
|
||||
eventType: eventTypeObject,
|
||||
locationOptions,
|
||||
availability,
|
||||
|
@ -1286,7 +1281,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
teamMembers,
|
||||
hasPaymentIntegration,
|
||||
currency,
|
||||
...(await serverSideTranslations(locale, ["common"])),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
import { ExternalLinkIcon } from "@heroicons/react/solid";
|
||||
import { GetServerSidePropsContext } from "next";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
|
||||
import { getSession } from "@lib/auth";
|
||||
import { getOrSetUserLocaleFromHeaders } from "@lib/core/i18n/i18n.utils";
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import prisma from "@lib/prisma";
|
||||
|
||||
import SettingsShell from "@components/SettingsShell";
|
||||
import Shell from "@components/Shell";
|
||||
|
@ -55,36 +50,3 @@ export default function Billing() {
|
|||
</Shell>
|
||||
);
|
||||
}
|
||||
|
||||
export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
const session = await getSession(context);
|
||||
const locale = await getOrSetUserLocaleFromHeaders(context.req);
|
||||
|
||||
if (!session) {
|
||||
return { redirect: { permanent: false, destination: "/auth/login" } };
|
||||
}
|
||||
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
email: session.user.email,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
name: true,
|
||||
email: true,
|
||||
bio: true,
|
||||
avatar: true,
|
||||
timeZone: true,
|
||||
weekStart: true,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
props: {
|
||||
session,
|
||||
user,
|
||||
...(await serverSideTranslations(locale, ["common"])),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
import { PlusIcon } from "@heroicons/react/outline";
|
||||
import { GetServerSidePropsContext } from "next";
|
||||
import { useSession } from "next-auth/client";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
import { getSession } from "@lib/auth";
|
||||
import { getOrSetUserLocaleFromHeaders } from "@lib/core/i18n/i18n.utils";
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import prisma from "@lib/prisma";
|
||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
import { trpc } from "@lib/trpc";
|
||||
import { Webhook } from "@lib/webhook";
|
||||
|
||||
import { Dialog, DialogClose, DialogContent, DialogHeader, DialogTrigger } from "@components/Dialog";
|
||||
|
@ -20,7 +15,8 @@ import Switch from "@components/ui/Switch";
|
|||
import EditWebhook from "@components/webhook/EditWebhook";
|
||||
import WebhookList from "@components/webhook/WebhookList";
|
||||
|
||||
export default function Embed(props: inferSSRProps<typeof getServerSideProps>) {
|
||||
export default function Embed() {
|
||||
const user = trpc.useQuery(["viewer.me"]).data;
|
||||
const [, loading] = useSession();
|
||||
const { t } = useLocale();
|
||||
|
||||
|
@ -51,11 +47,7 @@ export default function Embed(props: inferSSRProps<typeof getServerSideProps>) {
|
|||
getWebhooks();
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
const iframeTemplate = `<iframe src="${process.env.NEXT_PUBLIC_APP_URL}/${props.user?.username}" frameborder="0" allowfullscreen></iframe>`;
|
||||
const iframeTemplate = `<iframe src="${process.env.NEXT_PUBLIC_BASE_URL}/${user?.username}" frameborder="0" allowfullscreen></iframe>`;
|
||||
const htmlTemplate = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>${t(
|
||||
"schedule_a_meeting"
|
||||
)}</title><style>body {margin: 0;}iframe {height: calc(100vh - 4px);width: calc(100vw - 4px);box-sizing: border-box;}</style></head><body>${iframeTemplate}</body></html>`;
|
||||
|
@ -111,6 +103,10 @@ export default function Embed(props: inferSSRProps<typeof getServerSideProps>) {
|
|||
setEditWebhookEnabled(false);
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Shell heading={t("embed_and_webhooks")} subtitle={t("integrate_using_embed_or_webhooks")}>
|
||||
<SettingsShell>
|
||||
|
@ -280,41 +276,10 @@ export default function Embed(props: inferSSRProps<typeof getServerSideProps>) {
|
|||
</a>
|
||||
</div>
|
||||
)}
|
||||
{!!editWebhookEnabled && <EditWebhook webhook={webhookToEdit} onCloseEdit={onCloseEdit} />}
|
||||
{!!editWebhookEnabled && webhookToEdit && (
|
||||
<EditWebhook webhook={webhookToEdit} onCloseEdit={onCloseEdit} />
|
||||
)}
|
||||
</SettingsShell>
|
||||
</Shell>
|
||||
);
|
||||
}
|
||||
|
||||
export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
const session = await getSession(context);
|
||||
const locale = await getOrSetUserLocaleFromHeaders(context.req);
|
||||
|
||||
if (!session?.user?.email) {
|
||||
return { redirect: { permanent: false, destination: "/auth/login" } };
|
||||
}
|
||||
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
email: session?.user?.email,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
name: true,
|
||||
email: true,
|
||||
bio: true,
|
||||
avatar: true,
|
||||
timeZone: true,
|
||||
weekStart: true,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
props: {
|
||||
session,
|
||||
user,
|
||||
...(await serverSideTranslations(locale, ["common"])),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { InformationCircleIcon } from "@heroicons/react/outline";
|
||||
import crypto from "crypto";
|
||||
import { GetServerSidePropsContext } from "next";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
import { RefObject, useEffect, useRef, useState } from "react";
|
||||
import Select from "react-select";
|
||||
import TimezoneSelect from "react-timezone-select";
|
||||
|
@ -105,14 +104,12 @@ export default function Settings(props: Props) {
|
|||
{ value: "dark", label: t("dark") },
|
||||
];
|
||||
|
||||
const usernameRef = useRef<HTMLInputElement>(null);
|
||||
const nameRef = useRef<HTMLInputElement>(null);
|
||||
const descriptionRef = useRef<HTMLTextAreaElement>();
|
||||
const avatarRef = useRef<HTMLInputElement>(null);
|
||||
const hideBrandingRef = useRef<HTMLInputElement>(null);
|
||||
const [selectedTheme, setSelectedTheme] = useState<null | { value: string | null }>({
|
||||
value: props.user.theme,
|
||||
});
|
||||
const usernameRef = useRef<HTMLInputElement>(null!);
|
||||
const nameRef = useRef<HTMLInputElement>(null!);
|
||||
const descriptionRef = useRef<HTMLTextAreaElement>(null!);
|
||||
const avatarRef = useRef<HTMLInputElement>(null!);
|
||||
const hideBrandingRef = useRef<HTMLInputElement>(null!);
|
||||
const [selectedTheme, setSelectedTheme] = useState<undefined | { value: string; label: string }>(undefined);
|
||||
const [selectedTimeZone, setSelectedTimeZone] = useState({ value: props.user.timeZone });
|
||||
const [selectedWeekStartDay, setSelectedWeekStartDay] = useState({
|
||||
value: props.user.weekStart,
|
||||
|
@ -128,25 +125,12 @@ export default function Settings(props: Props) {
|
|||
|
||||
useEffect(() => {
|
||||
setSelectedTheme(
|
||||
props.user.theme ? themeOptions.find((theme) => theme.value === props.user.theme) : null
|
||||
props.user.theme ? themeOptions.find((theme) => theme.value === props.user.theme) : undefined
|
||||
);
|
||||
setSelectedWeekStartDay({ value: props.user.weekStart, label: props.user.weekStart });
|
||||
setSelectedLanguage({ value: props.localeProp, label: props.localeLabels[props.localeProp] });
|
||||
}, []);
|
||||
|
||||
const handleAvatarChange = (newAvatar) => {
|
||||
avatarRef.current.value = newAvatar;
|
||||
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
|
||||
window.HTMLInputElement.prototype,
|
||||
"value"
|
||||
).set;
|
||||
nativeInputValueSetter.call(avatarRef.current, newAvatar);
|
||||
const ev2 = new Event("input", { bubbles: true });
|
||||
avatarRef.current.dispatchEvent(ev2);
|
||||
updateProfileHandler(ev2);
|
||||
setImageSrc(newAvatar);
|
||||
};
|
||||
|
||||
async function updateProfileHandler(event) {
|
||||
event.preventDefault();
|
||||
|
||||
|
@ -273,7 +257,18 @@ export default function Settings(props: Props) {
|
|||
target="avatar"
|
||||
id="avatar-upload"
|
||||
buttonMsg={t("change_avatar")}
|
||||
handleAvatarChange={handleAvatarChange}
|
||||
handleAvatarChange={(newAvatar) => {
|
||||
avatarRef.current.value = newAvatar;
|
||||
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
|
||||
window.HTMLInputElement.prototype,
|
||||
"value"
|
||||
).set;
|
||||
nativeInputValueSetter.call(avatarRef.current, newAvatar);
|
||||
const ev2 = new Event("input", { bubbles: true });
|
||||
avatarRef.current.dispatchEvent(ev2);
|
||||
updateProfileHandler(ev2);
|
||||
setImageSrc(newAvatar);
|
||||
}}
|
||||
imageSrc={imageSrc}
|
||||
/>
|
||||
</div>
|
||||
|
@ -464,7 +459,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
...user,
|
||||
emailMd5: crypto.createHash("md5").update(user.email).digest("hex"),
|
||||
},
|
||||
...(await serverSideTranslations(locale, ["common"])),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,59 +1,22 @@
|
|||
import { GetServerSidePropsContext } from "next";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
import React from "react";
|
||||
|
||||
import { getSession } from "@lib/auth";
|
||||
import { getOrSetUserLocaleFromHeaders } from "@lib/core/i18n/i18n.utils";
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import prisma from "@lib/prisma";
|
||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import SettingsShell from "@components/SettingsShell";
|
||||
import Shell from "@components/Shell";
|
||||
import ChangePasswordSection from "@components/security/ChangePasswordSection";
|
||||
import TwoFactorAuthSection from "@components/security/TwoFactorAuthSection";
|
||||
|
||||
export default function Security({ user }: inferSSRProps<typeof getServerSideProps>) {
|
||||
export default function Security() {
|
||||
const user = trpc.useQuery(["viewer.me"]).data;
|
||||
const { t } = useLocale();
|
||||
return (
|
||||
<Shell heading={t("security")} subtitle={t("manage_account_security")}>
|
||||
<SettingsShell>
|
||||
<ChangePasswordSection />
|
||||
<TwoFactorAuthSection twoFactorEnabled={user.twoFactorEnabled} />
|
||||
<TwoFactorAuthSection twoFactorEnabled={user?.twoFactorEnabled} />
|
||||
</SettingsShell>
|
||||
</Shell>
|
||||
);
|
||||
}
|
||||
|
||||
export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
const session = await getSession(context);
|
||||
const locale = await getOrSetUserLocaleFromHeaders(context.req);
|
||||
|
||||
if (!session?.user?.id) {
|
||||
return { redirect: { permanent: false, destination: "/auth/login" } };
|
||||
}
|
||||
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
id: session.user.id,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
name: true,
|
||||
twoFactorEnabled: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return { redirect: { permanent: false, destination: "/auth/login" } };
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
session,
|
||||
user,
|
||||
...(await serverSideTranslations(locale, ["common"])),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
import { UsersIcon } from "@heroicons/react/outline";
|
||||
import { PlusIcon } from "@heroicons/react/solid";
|
||||
import { GetServerSideProps } from "next";
|
||||
import type { Session } from "next-auth";
|
||||
import { useSession } from "next-auth/client";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
import { getSession } from "@lib/auth";
|
||||
import { getOrSetUserLocaleFromHeaders } from "@lib/core/i18n/i18n.utils";
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import { Member } from "@lib/member";
|
||||
import { Team } from "@lib/team";
|
||||
|
@ -200,20 +195,3 @@ export default function Teams() {
|
|||
</Shell>
|
||||
);
|
||||
}
|
||||
|
||||
// Export the `session` prop to use sessions with Server Side Rendering
|
||||
export const getServerSideProps: GetServerSideProps<{ session: Session | null }> = async (context) => {
|
||||
const session = await getSession(context);
|
||||
const locale = await getOrSetUserLocaleFromHeaders(context.req);
|
||||
if (!session) {
|
||||
return { redirect: { permanent: false, destination: "/auth/login" } };
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
session,
|
||||
localeProp: locale,
|
||||
...(await serverSideTranslations(locale, ["common"])),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { ArrowRightIcon } from "@heroicons/react/solid";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { GetServerSidePropsContext } from "next";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
import Link from "next/link";
|
||||
import React from "react";
|
||||
|
||||
import { getOrSetUserLocaleFromHeaders } from "@lib/core/i18n/i18n.utils";
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import useTheme from "@lib/hooks/useTheme";
|
||||
import { useToggleQuery } from "@lib/hooks/useToggleQuery";
|
||||
|
@ -102,7 +100,6 @@ function TeamPage({ team }: inferSSRProps<typeof getServerSideProps>) {
|
|||
}
|
||||
|
||||
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
||||
const locale = await getOrSetUserLocaleFromHeaders(context.req);
|
||||
const slug = Array.isArray(context.query?.slug) ? context.query.slug.pop() : context.query.slug;
|
||||
|
||||
const userSelect = Prisma.validator<Prisma.UserSelect>()({
|
||||
|
@ -165,9 +162,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
|
||||
return {
|
||||
props: {
|
||||
localeProp: locale,
|
||||
team,
|
||||
...(await serverSideTranslations(locale, ["common"])),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { GetServerSidePropsContext } from "next";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
|
||||
import { asStringOrNull } from "@lib/asStringOrNull";
|
||||
import { getOrSetUserLocaleFromHeaders } from "@lib/core/i18n/i18n.utils";
|
||||
import prisma from "@lib/prisma";
|
||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
|
||||
|
@ -15,7 +13,6 @@ export default function TeamType(props: AvailabilityTeamPageProps) {
|
|||
}
|
||||
|
||||
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
||||
const locale = await getOrSetUserLocaleFromHeaders(context.req);
|
||||
const slugParam = asStringOrNull(context.query.slug);
|
||||
const typeParam = asStringOrNull(context.query.type);
|
||||
const dateParam = asStringOrNull(context.query.date);
|
||||
|
@ -81,7 +78,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
|
||||
return {
|
||||
props: {
|
||||
localeProp: locale,
|
||||
profile: {
|
||||
name: team.name,
|
||||
slug: team.slug,
|
||||
|
@ -91,7 +87,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
date: dateParam,
|
||||
eventType: eventTypeObject,
|
||||
workingHours,
|
||||
...(await serverSideTranslations(locale, ["common"])),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { GetServerSidePropsContext } from "next";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
import "react-phone-number-input/style.css";
|
||||
|
||||
import { asStringOrThrow } from "@lib/asStringOrNull";
|
||||
import { getOrSetUserLocaleFromHeaders } from "@lib/core/i18n/i18n.utils";
|
||||
import prisma from "@lib/prisma";
|
||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
|
||||
|
@ -16,7 +14,6 @@ export default function TeamBookingPage(props: TeamBookingPageProps) {
|
|||
}
|
||||
|
||||
export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
const locale = await getOrSetUserLocaleFromHeaders(context.req);
|
||||
const eventTypeId = parseInt(asStringOrThrow(context.query.type));
|
||||
if (typeof eventTypeId !== "number" || eventTypeId % 1 !== 0) {
|
||||
return {
|
||||
|
@ -89,7 +86,6 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|||
|
||||
return {
|
||||
props: {
|
||||
localeProp: locale,
|
||||
profile: {
|
||||
...eventTypeObject.team,
|
||||
slug: "team/" + eventTypeObject.slug,
|
||||
|
@ -98,7 +94,6 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|||
},
|
||||
eventType: eventTypeObject,
|
||||
booking,
|
||||
...(await serverSideTranslations(locale, ["common"])),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
"on": "on",
|
||||
"and": "and",
|
||||
"calendar_shows_busy_between": "Your calendar shows you as busy between",
|
||||
"calendar_no_busy_slots": "Your don't have busy slots in this date.",
|
||||
"troubleshoot": "Troubleshoot",
|
||||
"troubleshoot_description": "Understand why certain times are available and others are blocked.",
|
||||
"overview_of_day": "Here is an overview of your day on",
|
||||
|
|
|
@ -174,7 +174,7 @@
|
|||
"profile_updated_successfully": "Perfil Actualizado con Éxito",
|
||||
"your_user_profile_updated_successfully": "Su perfil de usuario se ha actualizado correctamente.",
|
||||
"user_cannot_found_db": "El usuario parece haber iniciado sesión pero no se puede encontrar en la base de datos",
|
||||
"embed_and_webhooks": "Insertar & Webhooks",
|
||||
"embed_and_webhooks": "Incrustrar y Webhooks",
|
||||
"enabled": "Activado",
|
||||
"disabled": "Desactivado",
|
||||
"billing": "Facturación",
|
||||
|
|
|
@ -3,6 +3,7 @@ import * as trpc from "@trpc/server";
|
|||
import { Maybe } from "@trpc/server";
|
||||
import * as trpcNext from "@trpc/server/adapters/next";
|
||||
import { NextApiRequest } from "next";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
|
||||
import { getSession, Session } from "@lib/auth";
|
||||
import { getLocaleFromHeaders } from "@lib/core/i18n/i18n.utils";
|
||||
|
@ -83,7 +84,9 @@ export const createContext = async ({ req, res }: trpcNext.CreateNextContextOpti
|
|||
|
||||
const user = await getUserFromSession({ session, req });
|
||||
const locale = user?.locale ?? getLocaleFromHeaders(req);
|
||||
const i18n = await serverSideTranslations(locale, ["common"]);
|
||||
return {
|
||||
i18n,
|
||||
prisma,
|
||||
session,
|
||||
user,
|
||||
|
|
|
@ -11,13 +11,14 @@ export function createRouter() {
|
|||
|
||||
export function createProtectedRouter() {
|
||||
return createRouter().middleware(({ ctx, next }) => {
|
||||
if (!ctx.user) {
|
||||
if (!ctx.user || !ctx.session) {
|
||||
throw new trpc.TRPCError({ code: "UNAUTHORIZED" });
|
||||
}
|
||||
return next({
|
||||
ctx: {
|
||||
...ctx,
|
||||
// infers that `user` is non-nullable to downstream procedures
|
||||
// infers that `user` and `session` are non-nullable to downstream procedures
|
||||
session: ctx.session,
|
||||
user: ctx.user,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -11,14 +11,31 @@ import { ALL_INTEGRATIONS } from "@lib/integrations/getIntegrations";
|
|||
import slugify from "@lib/slugify";
|
||||
|
||||
import { getCalendarAdapterOrNull } from "../../lib/calendarClient";
|
||||
import { createProtectedRouter } from "../createRouter";
|
||||
import { createProtectedRouter, createRouter } from "../createRouter";
|
||||
import { resizeBase64Image } from "../lib/resizeBase64Image";
|
||||
|
||||
const checkUsername =
|
||||
process.env.NEXT_PUBLIC_APP_URL === "https://cal.com" ? checkPremiumUsername : checkRegularUsername;
|
||||
|
||||
// things that unauthenticated users can query about themselves
|
||||
const publicViewerRouter = createRouter()
|
||||
.query("session", {
|
||||
resolve({ ctx }) {
|
||||
return ctx.session;
|
||||
},
|
||||
})
|
||||
.query("i18n", {
|
||||
async resolve({ ctx }) {
|
||||
const { locale, i18n } = ctx;
|
||||
return {
|
||||
i18n,
|
||||
locale,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
// routes only available to authenticated users
|
||||
export const viewerRouter = createProtectedRouter()
|
||||
const loggedInViewerRouter = createProtectedRouter()
|
||||
.query("me", {
|
||||
resolve({ ctx }) {
|
||||
const {
|
||||
|
@ -34,6 +51,7 @@ export const viewerRouter = createProtectedRouter()
|
|||
avatar,
|
||||
createdDate,
|
||||
completedOnboarding,
|
||||
twoFactorEnabled,
|
||||
} = ctx.user;
|
||||
const me = {
|
||||
id,
|
||||
|
@ -47,6 +65,7 @@ export const viewerRouter = createProtectedRouter()
|
|||
avatar,
|
||||
createdDate,
|
||||
completedOnboarding,
|
||||
twoFactorEnabled,
|
||||
};
|
||||
return me;
|
||||
},
|
||||
|
@ -251,3 +270,5 @@ export const viewerRouter = createProtectedRouter()
|
|||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const viewerRouter = createRouter().merge(publicViewerRouter).merge(loggedInViewerRouter);
|
||||
|
|
Loading…
Reference in a new issue