mirror of
https://github.com/lukevella/rallly.git
synced 2025-07-09 12:37:29 +02:00
⚡️ Use nextjs layout feature
This commit is contained in:
parent
02ef9000a7
commit
c2c000f770
9 changed files with 168 additions and 154 deletions
|
@ -17,7 +17,6 @@ import {
|
||||||
UserDetailsData,
|
UserDetailsData,
|
||||||
UserDetailsForm,
|
UserDetailsForm,
|
||||||
} from "./forms";
|
} from "./forms";
|
||||||
import StandardLayout from "./layouts/standard-layout";
|
|
||||||
import Steps from "./steps";
|
import Steps from "./steps";
|
||||||
import { useUser } from "./user-provider";
|
import { useUser } from "./user-provider";
|
||||||
|
|
||||||
|
@ -144,82 +143,80 @@ const Page: NextPage<CreatePollPageProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StandardLayout>
|
<div className="max-w-full px-3 pb-3 sm:p-4">
|
||||||
<div className="max-w-full px-3 pb-3 sm:p-4">
|
<div className="max-w-full">
|
||||||
<div className="max-w-full">
|
<div className="max-w-full overflow-hidden rounded-lg border bg-white shadow-sm">
|
||||||
<div className="max-w-full overflow-hidden rounded-lg border bg-white shadow-sm">
|
<div className="flex justify-between border-b p-4">
|
||||||
<div className="flex justify-between border-b p-4">
|
<h1 className="m-0 text-xl font-semibold text-slate-800">
|
||||||
<h1 className="m-0 text-xl font-semibold text-slate-800">
|
{t("createNew")}
|
||||||
{t("createNew")}
|
</h1>
|
||||||
</h1>
|
<Steps current={currentStepIndex} total={steps.length} />
|
||||||
<Steps current={currentStepIndex} total={steps.length} />
|
</div>
|
||||||
</div>
|
<div className="">
|
||||||
<div className="">
|
{(() => {
|
||||||
{(() => {
|
switch (currentStepName) {
|
||||||
switch (currentStepName) {
|
case "eventDetails":
|
||||||
case "eventDetails":
|
return (
|
||||||
return (
|
<PollDetailsForm
|
||||||
<PollDetailsForm
|
className="max-w-full p-3 sm:p-4"
|
||||||
className="max-w-full p-3 sm:p-4"
|
name={currentStepName}
|
||||||
name={currentStepName}
|
defaultValues={formData?.eventDetails}
|
||||||
defaultValues={formData?.eventDetails}
|
onSubmit={handleSubmit}
|
||||||
onSubmit={handleSubmit}
|
onChange={handleChange}
|
||||||
onChange={handleChange}
|
/>
|
||||||
/>
|
);
|
||||||
);
|
case "options":
|
||||||
case "options":
|
return (
|
||||||
return (
|
<PollOptionsForm
|
||||||
<PollOptionsForm
|
className="grow"
|
||||||
className="grow"
|
name={currentStepName}
|
||||||
name={currentStepName}
|
defaultValues={formData?.options}
|
||||||
defaultValues={formData?.options}
|
onSubmit={handleSubmit}
|
||||||
onSubmit={handleSubmit}
|
onChange={handleChange}
|
||||||
onChange={handleChange}
|
title={formData.eventDetails?.title}
|
||||||
title={formData.eventDetails?.title}
|
/>
|
||||||
/>
|
);
|
||||||
);
|
case "userDetails":
|
||||||
case "userDetails":
|
return (
|
||||||
return (
|
<UserDetailsForm
|
||||||
<UserDetailsForm
|
className="grow p-4"
|
||||||
className="grow p-4"
|
name={currentStepName}
|
||||||
name={currentStepName}
|
defaultValues={formData?.userDetails}
|
||||||
defaultValues={formData?.userDetails}
|
onSubmit={handleSubmit}
|
||||||
onSubmit={handleSubmit}
|
onChange={handleChange}
|
||||||
onChange={handleChange}
|
/>
|
||||||
/>
|
);
|
||||||
);
|
}
|
||||||
}
|
})()}
|
||||||
})()}
|
<div className="flex w-full justify-end space-x-3 border-t bg-slate-50 px-4 py-3">
|
||||||
<div className="flex w-full justify-end space-x-3 border-t bg-slate-50 px-4 py-3">
|
{currentStepIndex > 0 ? (
|
||||||
{currentStepIndex > 0 ? (
|
|
||||||
<Button
|
|
||||||
disabled={isBusy}
|
|
||||||
onClick={() => {
|
|
||||||
setFormData({
|
|
||||||
...persistedFormData,
|
|
||||||
currentStep: currentStepIndex - 1,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t("back")}
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
<Button
|
<Button
|
||||||
form={currentStepName}
|
disabled={isBusy}
|
||||||
loading={isBusy}
|
onClick={() => {
|
||||||
htmlType="submit"
|
setFormData({
|
||||||
type="primary"
|
...persistedFormData,
|
||||||
|
currentStep: currentStepIndex - 1,
|
||||||
|
});
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{currentStepIndex < steps.length - 1
|
{t("back")}
|
||||||
? t("continue")
|
|
||||||
: t("createPoll")}
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
) : null}
|
||||||
|
<Button
|
||||||
|
form={currentStepName}
|
||||||
|
loading={isBusy}
|
||||||
|
htmlType="submit"
|
||||||
|
type="primary"
|
||||||
|
>
|
||||||
|
{currentStepIndex < steps.length - 1
|
||||||
|
? t("continue")
|
||||||
|
: t("createPoll")}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</StandardLayout>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
import { MobileNavigation } from "./standard-layout/mobile-navigation";
|
|
||||||
|
|
||||||
export const ParticipantLayout = ({
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children?: React.ReactNode;
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<div className="bg-pattern min-h-full sm:space-y-8">
|
|
||||||
<MobileNavigation />
|
|
||||||
<div className="mx-auto max-w-4xl space-y-4 px-3 pb-8">
|
|
||||||
<div>{children}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -2,19 +2,28 @@ import React from "react";
|
||||||
|
|
||||||
import { DayjsProvider } from "@/utils/dayjs";
|
import { DayjsProvider } from "@/utils/dayjs";
|
||||||
|
|
||||||
|
import { NextPageWithLayout } from "../../types";
|
||||||
|
import { UserProvider } from "../user-provider";
|
||||||
import { MobileNavigation } from "./standard-layout/mobile-navigation";
|
import { MobileNavigation } from "./standard-layout/mobile-navigation";
|
||||||
|
|
||||||
const StandardLayout: React.VoidFunctionComponent<{
|
const StandardLayout: React.VoidFunctionComponent<{
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}> = ({ children, ...rest }) => {
|
}> = ({ children, ...rest }) => {
|
||||||
return (
|
return (
|
||||||
<DayjsProvider>
|
<UserProvider>
|
||||||
<div className="bg-pattern relative min-h-full" {...rest}>
|
<DayjsProvider>
|
||||||
<MobileNavigation />
|
<div className={"bg-pattern relative min-h-full"} {...rest}>
|
||||||
<div className="mx-auto max-w-4xl">{children}</div>
|
<MobileNavigation />
|
||||||
</div>
|
<div className="mx-auto max-w-4xl">{children}</div>
|
||||||
</DayjsProvider>
|
</div>
|
||||||
|
</DayjsProvider>
|
||||||
|
</UserProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default StandardLayout;
|
export default StandardLayout;
|
||||||
|
|
||||||
|
export const getStandardLayout: NextPageWithLayout["getLayout"] =
|
||||||
|
function getLayout(page) {
|
||||||
|
return <StandardLayout>{page}</StandardLayout>;
|
||||||
|
};
|
||||||
|
|
|
@ -16,7 +16,9 @@ import Maintenance from "@/components/maintenance";
|
||||||
|
|
||||||
import { useCrispChat } from "../components/crisp-chat";
|
import { useCrispChat } from "../components/crisp-chat";
|
||||||
import ModalProvider from "../components/modal/modal-provider";
|
import ModalProvider from "../components/modal/modal-provider";
|
||||||
|
import { NextPageWithLayout } from "../types";
|
||||||
import { absoluteUrl } from "../utils/absolute-url";
|
import { absoluteUrl } from "../utils/absolute-url";
|
||||||
|
import { UserSession } from "../utils/auth";
|
||||||
import { trpcNext } from "../utils/trpc";
|
import { trpcNext } from "../utils/trpc";
|
||||||
|
|
||||||
const inter = Inter({
|
const inter = Inter({
|
||||||
|
@ -29,7 +31,15 @@ const noto = Noto_Sans_Mono({
|
||||||
display: "swap",
|
display: "swap",
|
||||||
});
|
});
|
||||||
|
|
||||||
const MyApp: NextPage<AppProps> = ({ Component, pageProps }) => {
|
type PageProps = {
|
||||||
|
user: UserSession;
|
||||||
|
};
|
||||||
|
|
||||||
|
type AppPropsWithLayout = AppProps<PageProps> & {
|
||||||
|
Component: NextPageWithLayout<PageProps>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const MyApp: NextPage<AppPropsWithLayout> = ({ Component, pageProps }) => {
|
||||||
useCrispChat();
|
useCrispChat();
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
@ -43,6 +53,8 @@ const MyApp: NextPage<AppProps> = ({ Component, pageProps }) => {
|
||||||
return <Maintenance />;
|
return <Maintenance />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getLayout = Component.getLayout ?? ((page) => page);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DefaultSeo
|
<DefaultSeo
|
||||||
|
@ -77,9 +89,7 @@ const MyApp: NextPage<AppProps> = ({ Component, pageProps }) => {
|
||||||
--font-noto: ${noto.style.fontFamily};
|
--font-noto: ${noto.style.fontFamily};
|
||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
<ModalProvider>
|
<ModalProvider>{getLayout(<Component {...pageProps} />)}</ModalProvider>
|
||||||
<Component {...pageProps} />
|
|
||||||
</ModalProvider>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
import { GetServerSideProps, NextPage } from "next";
|
import { GetServerSideProps } from "next";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useTranslation } from "next-i18next";
|
import { useTranslation } from "next-i18next";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
import FullPageLoader from "@/components/full-page-loader";
|
import FullPageLoader from "@/components/full-page-loader";
|
||||||
import StandardLayout from "@/components/layouts/standard-layout";
|
import { getStandardLayout } from "@/components/layouts/standard-layout";
|
||||||
import { ParticipantsProvider } from "@/components/participants-provider";
|
import { ParticipantsProvider } from "@/components/participants-provider";
|
||||||
import { Poll } from "@/components/poll";
|
import { Poll } from "@/components/poll";
|
||||||
import { PollContextProvider } from "@/components/poll-context";
|
import { PollContextProvider } from "@/components/poll-context";
|
||||||
import { withSession } from "@/components/user-provider";
|
|
||||||
import { withSessionSsr } from "@/utils/auth";
|
import { withSessionSsr } from "@/utils/auth";
|
||||||
import { trpcNext } from "@/utils/trpc";
|
import { trpcNext } from "@/utils/trpc";
|
||||||
import { withPageTranslations } from "@/utils/with-page-translations";
|
import { withPageTranslations } from "@/utils/with-page-translations";
|
||||||
|
|
||||||
import { AdminControls } from "../../components/admin-control";
|
import { AdminControls } from "../../components/admin-control";
|
||||||
import ModalProvider from "../../components/modal/modal-provider";
|
import ModalProvider from "../../components/modal/modal-provider";
|
||||||
|
import { NextPageWithLayout } from "../../types";
|
||||||
|
|
||||||
const PollPageLoader: NextPage = () => {
|
const Page: NextPageWithLayout = () => {
|
||||||
const { query } = useRouter();
|
const { query } = useRouter();
|
||||||
const { t } = useTranslation("app");
|
const { t } = useTranslation("app");
|
||||||
const urlId = query.urlId as string;
|
const urlId = query.urlId as string;
|
||||||
|
@ -34,17 +33,15 @@ const PollPageLoader: NextPage = () => {
|
||||||
<meta name="robots" content="noindex,nofollow" />
|
<meta name="robots" content="noindex,nofollow" />
|
||||||
</Head>
|
</Head>
|
||||||
<ParticipantsProvider pollId={poll.id}>
|
<ParticipantsProvider pollId={poll.id}>
|
||||||
<StandardLayout>
|
<PollContextProvider poll={poll} urlId={urlId} admin={true}>
|
||||||
<PollContextProvider poll={poll} urlId={urlId} admin={true}>
|
<ModalProvider>
|
||||||
<ModalProvider>
|
<div className="flex flex-col space-y-3 p-3 sm:space-y-4 sm:p-4">
|
||||||
<div className="flex flex-col space-y-3 p-3 sm:space-y-4 sm:p-4">
|
<AdminControls>
|
||||||
<AdminControls>
|
<Poll />
|
||||||
<Poll />
|
</AdminControls>
|
||||||
</AdminControls>
|
</div>
|
||||||
</div>
|
</ModalProvider>
|
||||||
</ModalProvider>
|
</PollContextProvider>
|
||||||
</PollContextProvider>
|
|
||||||
</StandardLayout>
|
|
||||||
</ParticipantsProvider>
|
</ParticipantsProvider>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -53,6 +50,8 @@ const PollPageLoader: NextPage = () => {
|
||||||
return <FullPageLoader>{t("loading")}</FullPageLoader>;
|
return <FullPageLoader>{t("loading")}</FullPageLoader>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Page.getLayout = getStandardLayout;
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = withSessionSsr(
|
export const getServerSideProps: GetServerSideProps = withSessionSsr(
|
||||||
withPageTranslations(["common", "app", "errors"]),
|
withPageTranslations(["common", "app", "errors"]),
|
||||||
{
|
{
|
||||||
|
@ -64,4 +63,4 @@ export const getServerSideProps: GetServerSideProps = withSessionSsr(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export default withSession(PollPageLoader);
|
export default Page;
|
||||||
|
|
|
@ -4,11 +4,12 @@ import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import CreatePoll from "@/components/create-poll";
|
import CreatePoll from "@/components/create-poll";
|
||||||
|
|
||||||
import { withSession } from "../components/user-provider";
|
import StandardLayout from "../components/layouts/standard-layout";
|
||||||
|
import { NextPageWithLayout } from "../types";
|
||||||
import { withSessionSsr } from "../utils/auth";
|
import { withSessionSsr } from "../utils/auth";
|
||||||
import { withPageTranslations } from "../utils/with-page-translations";
|
import { withPageTranslations } from "../utils/with-page-translations";
|
||||||
|
|
||||||
const Page = () => {
|
const Page: NextPageWithLayout = () => {
|
||||||
const { t } = useTranslation("app");
|
const { t } = useTranslation("app");
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -20,7 +21,12 @@ const Page = () => {
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default withSession(Page);
|
|
||||||
|
Page.getLayout = function getLayout(page) {
|
||||||
|
return <StandardLayout>{page}</StandardLayout>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Page;
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = withSessionSsr(
|
export const getServerSideProps: GetServerSideProps = withSessionSsr(
|
||||||
withPageTranslations(["common", "app"]),
|
withPageTranslations(["common", "app"]),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { GetServerSideProps, NextPage } from "next";
|
import { GetServerSideProps } from "next";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
@ -7,16 +7,16 @@ import { useTranslation } from "next-i18next";
|
||||||
import { ParticipantsProvider } from "@/components/participants-provider";
|
import { ParticipantsProvider } from "@/components/participants-provider";
|
||||||
import { Poll } from "@/components/poll";
|
import { Poll } from "@/components/poll";
|
||||||
import { PollContextProvider } from "@/components/poll-context";
|
import { PollContextProvider } from "@/components/poll-context";
|
||||||
import { useUser, withSession } from "@/components/user-provider";
|
import { useUser } from "@/components/user-provider";
|
||||||
import { withSessionSsr } from "@/utils/auth";
|
import { withSessionSsr } from "@/utils/auth";
|
||||||
import { trpcNext } from "@/utils/trpc";
|
import { trpcNext } from "@/utils/trpc";
|
||||||
import { withPageTranslations } from "@/utils/with-page-translations";
|
import { withPageTranslations } from "@/utils/with-page-translations";
|
||||||
|
|
||||||
import { ParticipantLayout } from "../../components/layouts/participant-layout";
|
import StandardLayout from "../../components/layouts/standard-layout";
|
||||||
import ModalProvider from "../../components/modal/modal-provider";
|
import ModalProvider from "../../components/modal/modal-provider";
|
||||||
import { DayjsProvider } from "../../utils/dayjs";
|
import { NextPageWithLayout } from "../../types";
|
||||||
|
|
||||||
const Page: NextPage = () => {
|
const Page: NextPageWithLayout = () => {
|
||||||
const { query } = useRouter();
|
const { query } = useRouter();
|
||||||
const urlId = query.urlId as string;
|
const urlId = query.urlId as string;
|
||||||
|
|
||||||
|
@ -33,27 +33,23 @@ const Page: NextPage = () => {
|
||||||
<title>{poll.title}</title>
|
<title>{poll.title}</title>
|
||||||
<meta name="robots" content="noindex,nofollow" />
|
<meta name="robots" content="noindex,nofollow" />
|
||||||
</Head>
|
</Head>
|
||||||
<DayjsProvider>
|
<ParticipantsProvider pollId={poll.id}>
|
||||||
<ParticipantsProvider pollId={poll.id}>
|
<PollContextProvider poll={poll} urlId={urlId} admin={false}>
|
||||||
<ParticipantLayout>
|
<ModalProvider>
|
||||||
<PollContextProvider poll={poll} urlId={urlId} admin={false}>
|
<div className="space-y-3 sm:space-y-4">
|
||||||
<ModalProvider>
|
{user.id === poll.user.id ? (
|
||||||
<div className="space-y-3 sm:space-y-4">
|
<Link
|
||||||
{user.id === poll.user.id ? (
|
className="btn-default"
|
||||||
<Link
|
href={`/admin/${poll.adminUrlId}`}
|
||||||
className="btn-default"
|
>
|
||||||
href={`/admin/${poll.adminUrlId}`}
|
← {t("goToAdmin")}
|
||||||
>
|
</Link>
|
||||||
← {t("goToAdmin")}
|
) : null}
|
||||||
</Link>
|
<Poll />
|
||||||
) : null}
|
</div>
|
||||||
<Poll />
|
</ModalProvider>
|
||||||
</div>
|
</PollContextProvider>
|
||||||
</ModalProvider>
|
</ParticipantsProvider>
|
||||||
</PollContextProvider>
|
|
||||||
</ParticipantLayout>
|
|
||||||
</ParticipantsProvider>
|
|
||||||
</DayjsProvider>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -61,6 +57,10 @@ const Page: NextPage = () => {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Page.getLayout = function getLayout(page) {
|
||||||
|
return <StandardLayout>{page}</StandardLayout>;
|
||||||
|
};
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = withSessionSsr(
|
export const getServerSideProps: GetServerSideProps = withSessionSsr(
|
||||||
withPageTranslations(["common", "app", "errors"]),
|
withPageTranslations(["common", "app", "errors"]),
|
||||||
{
|
{
|
||||||
|
@ -72,4 +72,4 @@ export const getServerSideProps: GetServerSideProps = withSessionSsr(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export default withSession(Page);
|
export default Page;
|
||||||
|
|
|
@ -1,20 +1,16 @@
|
||||||
import { NextPage } from "next";
|
|
||||||
|
|
||||||
import { withSessionSsr } from "@/utils/auth";
|
import { withSessionSsr } from "@/utils/auth";
|
||||||
|
|
||||||
import StandardLayout from "../components/layouts/standard-layout";
|
import { getStandardLayout } from "../components/layouts/standard-layout";
|
||||||
import { Profile } from "../components/profile";
|
import { Profile } from "../components/profile";
|
||||||
import { withSession } from "../components/user-provider";
|
import { NextPageWithLayout } from "../types";
|
||||||
import { withPageTranslations } from "../utils/with-page-translations";
|
import { withPageTranslations } from "../utils/with-page-translations";
|
||||||
|
|
||||||
const Page: NextPage = () => {
|
const Page: NextPageWithLayout = () => {
|
||||||
return (
|
return <Profile />;
|
||||||
<StandardLayout>
|
|
||||||
<Profile />
|
|
||||||
</StandardLayout>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Page.getLayout = getStandardLayout;
|
||||||
|
|
||||||
export const getServerSideProps = withSessionSsr(async (ctx) => {
|
export const getServerSideProps = withSessionSsr(async (ctx) => {
|
||||||
if (ctx.req.session.user.isGuest !== false) {
|
if (ctx.req.session.user.isGuest !== false) {
|
||||||
return {
|
return {
|
||||||
|
@ -27,4 +23,4 @@ export const getServerSideProps = withSessionSsr(async (ctx) => {
|
||||||
return withPageTranslations(["common", "app"])(ctx);
|
return withPageTranslations(["common", "app"])(ctx);
|
||||||
});
|
});
|
||||||
|
|
||||||
export default withSession(Page);
|
export default Page;
|
||||||
|
|
13
src/types.ts
Normal file
13
src/types.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { NextPage } from "next";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export type ReactTag = keyof JSX.IntrinsicElements;
|
||||||
|
|
||||||
|
export type PropsOf<TTag extends ReactTag> = TTag extends React.ElementType
|
||||||
|
? React.ComponentProps<TTag>
|
||||||
|
: never;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
|
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
|
||||||
|
getLayout?: (page: React.ReactElement) => React.ReactNode;
|
||||||
|
};
|
Loading…
Add table
Add a link
Reference in a new issue