diff --git a/apps/web/app/not-found.tsx b/apps/web/app/not-found.tsx new file mode 100644 index 00000000000000..6bac953ad07131 --- /dev/null +++ b/apps/web/app/not-found.tsx @@ -0,0 +1,28 @@ +import NotFoundPage from "@pages/404"; +import { ssgInit } from "app/_trpc/ssgInit"; +import { headers } from "next/headers"; + +import PageWrapper from "@components/PageWrapperAppDir"; + +const getProps = async () => { + const ssg = await ssgInit(); + + return { + dehydratedState: await ssg.dehydrate(), + }; +}; + +const NotFound = async () => { + const nonce = headers().get("x-nonce") ?? undefined; + const props = await getProps(); + + return ( + + + + ); +}; + +export const dynamic = "force-static"; + +export default NotFound; diff --git a/apps/web/lib/app-providers-app-dir.tsx b/apps/web/lib/app-providers-app-dir.tsx index 2b2d57d2ea64fd..87389f926f500d 100644 --- a/apps/web/lib/app-providers-app-dir.tsx +++ b/apps/web/lib/app-providers-app-dir.tsx @@ -62,8 +62,12 @@ const CustomI18nextProvider = (props: { children: React.ReactElement; i18n?: SSR // @TODO const session = useSession(); + + // window.document.documentElement.lang can be empty in some cases, for instance when we rendering GlobalError (not-found) page. const locale = - session?.data?.user.locale ?? typeof window !== "undefined" ? window.document.documentElement.lang : "en"; + session?.data?.user.locale ?? typeof window !== "undefined" + ? window.document.documentElement.lang || "en" + : "en"; useEffect(() => { try { diff --git a/apps/web/next.config.js b/apps/web/next.config.js index 452838e30d6c7a..1964002ee29aa5 100644 --- a/apps/web/next.config.js +++ b/apps/web/next.config.js @@ -155,7 +155,16 @@ const matcherConfigUserTypeEmbedRoute = { /** @type {import("next").NextConfig} */ const nextConfig = { experimental: { - serverComponentsExternalPackages: ["next-i18next"], + serverComponentsExternalPackages: [ + "next-i18next", + "auth0", + "typeorm", + "@boxyhq", + "@ewsjs", + "handlebars", + "ews-javascript-api", + "react-awesome-query-builder", + ], }, i18n: { ...i18n, @@ -184,6 +193,7 @@ const nextConfig = { "@calcom/trpc", "@calcom/ui", "lucide-react", + "@formkit/auto-animate", ], modularizeImports: { "@calcom/ui/components/icon": { @@ -470,6 +480,11 @@ const nextConfig = { destination: "/bookings/upcoming", permanent: true, }, + { + source: "/future/bookings", + destination: "/future/bookings/upcoming", + permanent: true, + }, { source: "/call/:path*", destination: "/video/:path*", diff --git a/apps/web/pages/404.tsx b/apps/web/pages/404.tsx index 12cb57ac7d2784..e46222e51373cb 100644 --- a/apps/web/pages/404.tsx +++ b/apps/web/pages/404.tsx @@ -1,3 +1,5 @@ +"use client"; + import type { GetStaticPropsContext } from "next"; import Link from "next/link"; import { usePathname } from "next/navigation"; @@ -58,7 +60,7 @@ export default function Custom404() { }); const [routerUsername] = pathname?.replace("%20", "-").split(/[?#]/) ?? []; - if (routerUsername && (!isValidOrgDomain || !currentOrgDomain)) { + if (!isValidOrgDomain || !currentOrgDomain) { const splitPath = routerUsername.split("/"); if (splitPath[1] === "team" && splitPath.length === 3) { // Accessing a non-existent team diff --git a/packages/ui/components/errorBoundary/ErrorBoundary.tsx b/packages/ui/components/errorBoundary/ErrorBoundary.tsx index a1523ae3299a1f..f6c7f8e652b613 100644 --- a/packages/ui/components/errorBoundary/ErrorBoundary.tsx +++ b/packages/ui/components/errorBoundary/ErrorBoundary.tsx @@ -17,6 +17,15 @@ class ErrorBoundary extends React.Component< } render() { + // do not intercept next-not-found error, allow displaying not-found.tsx page when notFound() is thrown on server side + if ( + this.state.error !== null && + "digest" in this.state.error && + this.state.error.digest === "NEXT_NOT_FOUND" + ) { + return this.props.children; + } + if (this.state.errorInfo) { // Error path return (