new changes
This commit is contained in:
258
src/components/Layouts/DashboardLayout.tsx
Normal file
258
src/components/Layouts/DashboardLayout.tsx
Normal file
@@ -0,0 +1,258 @@
|
||||
import { Fragment, useState } from "react";
|
||||
import { Dialog, Menu, Transition } from "@headlessui/react";
|
||||
import {
|
||||
Bars3CenterLeftIcon,
|
||||
CogIcon,
|
||||
HomeIcon,
|
||||
XMarkIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
} from "@heroicons/react/20/solid";
|
||||
import { useUser } from "@supabase/auth-helpers-react";
|
||||
|
||||
const navigation = [
|
||||
{ name: "Home", href: "#", icon: HomeIcon, current: true },
|
||||
{ name: "Settings", href: "#", icon: CogIcon, current: false },
|
||||
];
|
||||
|
||||
//@ts-ignore
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
|
||||
export default function DashboardLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
const user = useUser()
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="min-h-full">
|
||||
<Transition.Root show={sidebarOpen} as={Fragment}>
|
||||
<Dialog
|
||||
as="div"
|
||||
className="relative z-40 lg:hidden"
|
||||
onClose={setSidebarOpen}
|
||||
>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="transition-opacity ease-linear duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="transition-opacity ease-linear duration-300"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-gray-600 bg-opacity-75" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-40 flex">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="transition ease-in-out duration-300 transform"
|
||||
enterFrom="-translate-x-full"
|
||||
enterTo="translate-x-0"
|
||||
leave="transition ease-in-out duration-300 transform"
|
||||
leaveFrom="translate-x-0"
|
||||
leaveTo="-translate-x-full"
|
||||
>
|
||||
<Dialog.Panel className="relative flex w-full max-w-xs flex-1 flex-col bg-white pb-4 pt-5">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-in-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in-out duration-300"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="absolute right-0 top-0 -mr-12 pt-2">
|
||||
<button
|
||||
type="button"
|
||||
className="ml-1 flex h-10 w-10 items-center justify-center rounded-full focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"
|
||||
onClick={() => setSidebarOpen(false)}
|
||||
>
|
||||
<span className="sr-only">Close sidebar</span>
|
||||
<XMarkIcon
|
||||
className="h-6 w-6 text-white"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</Transition.Child>
|
||||
<div className="flex flex-shrink-0 items-center px-4">
|
||||
<img
|
||||
className="h-10 w-auto"
|
||||
src="/logo.png"
|
||||
alt="PageAssist"
|
||||
/>
|
||||
</div>
|
||||
<nav
|
||||
className="mt-5 h-full flex-shrink-0 divide-y divide-gray-200 overflow-y-auto"
|
||||
aria-label="Sidebar"
|
||||
>
|
||||
<div className="space-y-1 px-2">
|
||||
{navigation.map((item) => (
|
||||
<a
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
className={classNames(
|
||||
item.current
|
||||
? "bg-gray-100 text-gray-900"
|
||||
: "text-gray-600 hover:bg-gray-50 hover:text-gray-900",
|
||||
"group flex items-center rounded-md px-2 py-2 text-sm font-medium"
|
||||
)}
|
||||
aria-current={item.current ? "page" : undefined}
|
||||
>
|
||||
<item.icon
|
||||
className={classNames(
|
||||
item.current
|
||||
? "text-gray-500"
|
||||
: "text-gray-400 group-hover:text-gray-500",
|
||||
"mr-3 h-6 w-6 flex-shrink-0"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{item.name}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
<div className="w-14 flex-shrink-0" aria-hidden="true">
|
||||
{/* Dummy element to force sidebar to shrink to fit close icon */}
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
|
||||
<div className="hidden lg:fixed lg:inset-y-0 lg:flex lg:w-64 lg:flex-col">
|
||||
<div className="flex flex-grow flex-col overflow-y-auto bg-white pb-4 pt-5 border-r border-gray-200">
|
||||
<div className="flex flex-shrink-0 items-center px-4">
|
||||
<img className="h-10 w-auto" src="/logo.png" alt="PageAssist" />
|
||||
</div>
|
||||
<nav
|
||||
className="mt-5 flex flex-1 flex-col divide-y divide-gray-200 overflow-y-auto"
|
||||
aria-label="Sidebar"
|
||||
>
|
||||
<div className="space-y-1 px-2">
|
||||
{navigation.map((item) => (
|
||||
<a
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
className={classNames(
|
||||
item.current ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900',
|
||||
'group flex items-center px-2 py-2 text-sm font-medium rounded-md'
|
||||
)}
|
||||
aria-current={item.current ? "page" : undefined}
|
||||
>
|
||||
<item.icon
|
||||
className={classNames(
|
||||
item.current ? 'text-gray-500' : 'text-gray-400 group-hover:text-gray-500',
|
||||
'mr-3 flex-shrink-0 h-6 w-6'
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{item.name}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-1 flex-col lg:pl-64">
|
||||
<div className="flex h-16 flex-shrink-0 border-b border-gray-200 bg-white lg:border-none">
|
||||
<button
|
||||
type="button"
|
||||
className="border-r border-gray-200 px-4 text-gray-400 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-gray-200 lg:hidden"
|
||||
onClick={() => setSidebarOpen(true)}
|
||||
>
|
||||
<span className="sr-only">Open sidebar</span>
|
||||
<Bars3CenterLeftIcon className="h-6 w-6" aria-hidden="true" />
|
||||
</button>
|
||||
{/* Search bar */}
|
||||
<div className="flex flex-1 justify-end px-4 sm:px-6 lg:mx-auto lg:max-w-6xl lg:px-8">
|
||||
<div className="ml-4 flex items-center md:ml-6">
|
||||
{/* Profile dropdown */}
|
||||
<Menu as="div" className="relative ml-3">
|
||||
<div>
|
||||
<Menu.Button className="flex max-w-xs items-center rounded-full bg-white text-sm focus:outline-none focus:ring-2 focus:ring-gray-200 focus:ring-offset-2 lg:rounded-md lg:p-2 lg:hover:bg-gray-50">
|
||||
<img
|
||||
className="h-8 w-8 rounded-full"
|
||||
src={`https://ui-avatars.com/api/?name=${user?.email}`}
|
||||
alt=""
|
||||
/>
|
||||
<span className="ml-3 hidden text-sm font-medium text-gray-700 lg:block">
|
||||
<span className="sr-only">Open user menu for </span>
|
||||
{
|
||||
user?.email
|
||||
}
|
||||
</span>
|
||||
<ChevronDownIcon
|
||||
className="ml-1 hidden h-5 w-5 flex-shrink-0 text-gray-400 lg:block"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Menu.Button>
|
||||
</div>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<a
|
||||
href="#"
|
||||
className={classNames(
|
||||
active ? "bg-gray-100" : "",
|
||||
"block px-4 py-2 text-sm text-gray-700"
|
||||
)}
|
||||
>
|
||||
Settings
|
||||
</a>
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<a
|
||||
href="#"
|
||||
className={classNames(
|
||||
active ? "bg-gray-100" : "",
|
||||
"block px-4 py-2 text-sm text-gray-700"
|
||||
)}
|
||||
|
||||
>
|
||||
Logout
|
||||
</a>
|
||||
)}
|
||||
</Menu.Item>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<main className="flex-1 pb-8">
|
||||
<div className="mt-8">
|
||||
<div className="mx-auto max-w-6xl px-4 sm:px-6 lg:px-8">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,48 @@
|
||||
import { type AppType } from "next/app";
|
||||
import { AppProps, type AppType } from "next/app";
|
||||
|
||||
import { api } from "~/utils/api";
|
||||
|
||||
import "~/styles/globals.css";
|
||||
|
||||
const MyApp: AppType = ({ Component, pageProps }) => {
|
||||
return <Component {...pageProps} />;
|
||||
};
|
||||
import { Poppins } from "next/font/google";
|
||||
import {
|
||||
createBrowserSupabaseClient,
|
||||
Session,
|
||||
} from "@supabase/auth-helpers-nextjs";
|
||||
import React from "react";
|
||||
import { SessionContextProvider } from "@supabase/auth-helpers-react";
|
||||
const poppins = Poppins({
|
||||
weight: ["400", "500", "600", "700", "800", "900"],
|
||||
style: ["normal"],
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
function MyApp({
|
||||
Component,
|
||||
pageProps,
|
||||
}: AppProps<{
|
||||
initialSession: Session;
|
||||
}>): JSX.Element {
|
||||
const [supabaseClient] = React.useState(() => createBrowserSupabaseClient());
|
||||
|
||||
return (
|
||||
<>
|
||||
<style jsx global>
|
||||
{`
|
||||
html,
|
||||
body {
|
||||
font-family: ${poppins.style.fontFamily} !important;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
<SessionContextProvider
|
||||
supabaseClient={supabaseClient}
|
||||
initialSession={pageProps.initialSession}
|
||||
>
|
||||
<Component {...pageProps} />
|
||||
</SessionContextProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default api.withTRPC(MyApp);
|
||||
|
||||
75
src/pages/auth.tsx
Normal file
75
src/pages/auth.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import { useSupabaseClient, useUser } from "@supabase/auth-helpers-react";
|
||||
import { type NextPage } from "next";
|
||||
import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import React from "react";
|
||||
import { Auth } from "@supabase/auth-ui-react";
|
||||
import { ThemeSupa, ThemeMinimal } from "@supabase/auth-ui-shared";
|
||||
|
||||
const AuthPage: NextPage = () => {
|
||||
const supabaseClient = useSupabaseClient();
|
||||
const user = useUser();
|
||||
const router = useRouter();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (user) {
|
||||
router.push("/dashboard");
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Get Started / Page Assist</title>
|
||||
</Head>
|
||||
<div className="relative isolate flex min-h-full flex-col justify-center overflow-hidden bg-white py-12 sm:px-6 lg:px-8">
|
||||
<svg
|
||||
className="absolute inset-0 -z-10 h-full w-full stroke-gray-200 [mask-image:radial-gradient(100%_100%_at_top_right,white,transparent)]"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<defs>
|
||||
<pattern
|
||||
id="0787a7c5-978c-4f66-83c7-11c213f99cb7"
|
||||
width={200}
|
||||
height={200}
|
||||
x="50%"
|
||||
y={-1}
|
||||
patternUnits="userSpaceOnUse"
|
||||
>
|
||||
<path d="M.5 200V.5H200" fill="none" />
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect
|
||||
width="100%"
|
||||
height="100%"
|
||||
strokeWidth={0}
|
||||
fill="url(#0787a7c5-978c-4f66-83c7-11c213f99cb7)"
|
||||
/>
|
||||
</svg>
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<img
|
||||
className="mx-auto h-12 w-auto"
|
||||
src="logo.png"
|
||||
alt="Page Assist"
|
||||
/>
|
||||
<h2 className="mt-6 text-center text-3xl font-bold tracking-tight text-gray-900">
|
||||
Page Assist
|
||||
</h2>
|
||||
</div>
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="px-4 py-8 shadow sm:rounded-lg sm:px-10">
|
||||
<Auth
|
||||
supabaseClient={supabaseClient}
|
||||
providers={[]}
|
||||
appearance={{ theme: ThemeSupa }}
|
||||
view="magic_link"
|
||||
showLinks={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AuthPage;
|
||||
31
src/pages/dashboard/index.tsx
Normal file
31
src/pages/dashboard/index.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { createServerSupabaseClient } from "@supabase/auth-helpers-nextjs";
|
||||
import { GetServerSideProps, NextPage } from "next";
|
||||
import DashboardLayout from "~/components/Layouts/DashboardLayout";
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
||||
const supabase = createServerSupabaseClient(ctx);
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession();
|
||||
|
||||
if (!session) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: "/auth",
|
||||
permanent: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
props: {},
|
||||
};
|
||||
};
|
||||
|
||||
const DashboardPage: NextPage = () => {
|
||||
return <DashboardLayout>
|
||||
a
|
||||
</DashboardLayout>;
|
||||
};
|
||||
|
||||
export default DashboardPage;
|
||||
Reference in New Issue
Block a user