Refactored the project now that there is a plan!
This commit is contained in:
@@ -6,12 +6,12 @@
|
||||
|
||||
# When adding additional env variables, the schema in /env/schema.mjs should be updated accordingly
|
||||
# Prisma
|
||||
DATABASE_URL=file:./db.sqlite
|
||||
DATABASE_URL="postgresql://postgres:{YOUR_PASSWORD}@localhost:5432/postgres"
|
||||
|
||||
# Next Auth
|
||||
# You can generate the secret via 'openssl rand -base64 32' on Linux
|
||||
# More info: https://next-auth.js.org/configuration/options#secret
|
||||
# NEXTAUTH_SECRET=
|
||||
NEXTAUTH_SECRET=
|
||||
NEXTAUTH_URL=http://localhost:3000
|
||||
|
||||
# Next Auth Providers
|
||||
|
||||
@@ -1,60 +1,60 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model Account {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
type String
|
||||
provider String
|
||||
providerAccountId String
|
||||
refresh_token String?
|
||||
access_token String?
|
||||
expires_at Int?
|
||||
token_type String?
|
||||
scope String?
|
||||
id_token String?
|
||||
session_state String?
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
type String
|
||||
provider String
|
||||
providerAccountId String
|
||||
refresh_token String?
|
||||
access_token String?
|
||||
expires_at Int?
|
||||
token_type String?
|
||||
scope String?
|
||||
id_token String?
|
||||
session_state String?
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([provider, providerAccountId])
|
||||
@@unique([provider, providerAccountId])
|
||||
}
|
||||
|
||||
model Session {
|
||||
id String @id @default(cuid())
|
||||
sessionToken String @unique
|
||||
userId String
|
||||
expires DateTime
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
id String @id @default(cuid())
|
||||
sessionToken String @unique
|
||||
userId String
|
||||
expires DateTime
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
name String?
|
||||
email String? @unique
|
||||
emailVerified DateTime?
|
||||
image String?
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
id String @id @default(cuid())
|
||||
name String?
|
||||
email String? @unique
|
||||
emailVerified DateTime?
|
||||
image String?
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
}
|
||||
|
||||
model VerificationToken {
|
||||
identifier String
|
||||
token String @unique
|
||||
expires DateTime
|
||||
identifier String
|
||||
token String @unique
|
||||
expires DateTime
|
||||
|
||||
@@unique([identifier, token])
|
||||
@@unique([identifier, token])
|
||||
}
|
||||
|
||||
model Project {
|
||||
id String @id
|
||||
title String
|
||||
isComplete Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime
|
||||
id String @id
|
||||
title String
|
||||
isComplete Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { typo } from "@styles/typography";
|
||||
import type { VariantProps } from "class-variance-authority";
|
||||
import { cx, cva } from "class-variance-authority";
|
||||
import Text, { text } from "@components/Text";
|
||||
|
||||
const button = cva(
|
||||
[
|
||||
@@ -13,8 +13,8 @@ const button = cva(
|
||||
{
|
||||
variants: {
|
||||
size: {
|
||||
default: "px-14 py-3",
|
||||
small: "px-10 py-2",
|
||||
default: cx(typo({ size: "2xl" }), "px-14 py-3"),
|
||||
small: cx(typo({ size: "lg" }), "px-10 py-2"),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -37,14 +37,7 @@ export const Button: React.FC<
|
||||
...buttonProps
|
||||
}) => {
|
||||
return (
|
||||
<button
|
||||
className={cx(
|
||||
button(variant),
|
||||
text({ size: variant.size === "default" ? "h4" : "h6" }),
|
||||
className
|
||||
)}
|
||||
{...buttonProps}
|
||||
>
|
||||
<button className={cx(button(variant), className)} {...buttonProps}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
@@ -63,13 +56,8 @@ export const Anchor: React.FC<
|
||||
...anchorProps
|
||||
}) => {
|
||||
return (
|
||||
<a {...anchorProps}>
|
||||
<Text
|
||||
styleLike={variant.size === "default" ? "h4" : "h6"}
|
||||
className={cx(button(variant), className)}
|
||||
>
|
||||
{children}
|
||||
</Text>
|
||||
<a {...anchorProps} className={cx(button(variant), className)}>
|
||||
{children}
|
||||
</a>
|
||||
// <div className={cx(button(variant), className)}>
|
||||
// <a
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
const Divider: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
|
||||
import type { Children } from "@utils/types/props";
|
||||
|
||||
const Divider: React.FC<Children> = ({ children }) => {
|
||||
if (children === undefined)
|
||||
return <div className="container my-24 h-1.5 border-y-2 border-y-fg/10" />;
|
||||
|
||||
|
||||
12
src/components/Footer.tsx
Normal file
12
src/components/Footer.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import { typo } from "@styles/typography";
|
||||
import React from "react";
|
||||
|
||||
export const Footer: React.FC = () => {
|
||||
return (
|
||||
<footer className="container flex flex-row justify-center py-32">
|
||||
{/* <nav> */}
|
||||
<h2 className={typo({ tag: "h2" })}>Footer goes here!</h2>
|
||||
{/* </nav> */}
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { cva, cx } from "class-variance-authority";
|
||||
import { Anchor, Button } from "@components/Button";
|
||||
import Text from "@components/Text";
|
||||
import { Button } from "@components/Button";
|
||||
import { signIn } from "next-auth/react";
|
||||
import { typo } from "@styles/typography";
|
||||
|
||||
const header = cva(
|
||||
["fixed top-0 z-50 w-full transition-all duration-300 ease-in-out"],
|
||||
@@ -16,7 +16,7 @@ const header = cva(
|
||||
}
|
||||
);
|
||||
|
||||
const HomeHeader: React.FC = () => {
|
||||
export const Header: React.FC = () => {
|
||||
const [isLargeBar, setIsLargeBar] = useState(true);
|
||||
|
||||
// Scroll Listener
|
||||
@@ -36,9 +36,7 @@ const HomeHeader: React.FC = () => {
|
||||
});
|
||||
|
||||
const FlexDivider = () => (
|
||||
<Text tag="span" styleLike="h4" className="text-fg/25">
|
||||
||
|
||||
</Text>
|
||||
<span className={cx(typo({ tag: "h4" }), "text-fg/25")}>||</span>
|
||||
);
|
||||
|
||||
const Background = () => (
|
||||
@@ -59,18 +57,19 @@ const HomeHeader: React.FC = () => {
|
||||
>
|
||||
<Background />
|
||||
<nav className="container flex flex-row items-center justify-between gap-x-4">
|
||||
<a href="#">
|
||||
<Text styleLike="h4" className="min-w-max text-primary">
|
||||
<span className="text-fg">||</span> Parallel
|
||||
</Text>
|
||||
<a
|
||||
href="#"
|
||||
className={cx(typo({ tag: "h4" }), "min-w-max text-primary")}
|
||||
>
|
||||
<span className="text-fg">||</span> Parallel
|
||||
</a>
|
||||
<div className="flex flex-row items-center gap-x-4 sm:gap-x-8">
|
||||
<a href="#">
|
||||
<Text weight="medium">About Us</Text>
|
||||
<a href="#" className={cx(typo({ tag: "p" }), "font-medium")}>
|
||||
About Us
|
||||
</a>
|
||||
<FlexDivider />
|
||||
<a href="#">
|
||||
<Text weight="medium">Premium</Text>
|
||||
<a href="#" className={cx(typo({ tag: "p" }), "font-medium")}>
|
||||
Premium
|
||||
</a>
|
||||
</div>
|
||||
<Button variant={{ size: "small" }} onClick={() => void signIn()}>
|
||||
@@ -80,5 +79,3 @@ const HomeHeader: React.FC = () => {
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
export default HomeHeader;
|
||||
@@ -1,18 +0,0 @@
|
||||
import React from "react";
|
||||
import { cva } from "class-variance-authority";
|
||||
import { Button } from "@components/Button";
|
||||
import Text from "@components/Text";
|
||||
|
||||
const footer = cva();
|
||||
|
||||
const HomeFooter: React.FC = () => {
|
||||
return (
|
||||
<footer className="container flex flex-row justify-center py-32">
|
||||
{/* <nav> */}
|
||||
<Text styleLike="h1">Footer goes here!</Text>
|
||||
{/* </nav> */}
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default HomeFooter;
|
||||
@@ -1,89 +0,0 @@
|
||||
import React from "react";
|
||||
import { cva } from "class-variance-authority";
|
||||
import type { Tag, Weight } from "@utils/types/tw";
|
||||
import Link from "next/link";
|
||||
|
||||
export const text = cva("", {
|
||||
variants: {
|
||||
size: {
|
||||
p: "text-sm md:text-base 2xl:text-lg",
|
||||
h1: "text-3xl md:text-4xl 2xl:text-5xl",
|
||||
h2: "text-2xl md:text-3xl 2xl:text-4xl",
|
||||
h3: "text-xl md:text-2xl 2xl:text-3xl",
|
||||
h4: "text-lg md:text-xl 2xl:text-2xl",
|
||||
h5: "text-base md:text-lg 2xl:text-xl",
|
||||
h6: "text-sm md:text-base 2xl:text-lg",
|
||||
},
|
||||
weight: {
|
||||
p: "font-normal",
|
||||
h1: "font-bold",
|
||||
h2: "font-bold",
|
||||
h3: "font-bold",
|
||||
h4: "font-bold",
|
||||
h5: "font-semibold",
|
||||
h6: "font-semibold",
|
||||
thin: "font-thin",
|
||||
extralight: "font-extralight",
|
||||
light: "font-light",
|
||||
normal: "font-normal",
|
||||
medium: "font-medium",
|
||||
semibold: "font-semibold",
|
||||
bold: "font-bold",
|
||||
extrabold: "font-extrabold",
|
||||
black: "font-black",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
tag?: Tag | "span" | "a";
|
||||
styleLike?: Tag;
|
||||
href?: string;
|
||||
// Override default tag styling
|
||||
size?: Tag;
|
||||
weight?: Weight;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Text: React.FC<Props> = ({
|
||||
children,
|
||||
tag = "p",
|
||||
styleLike,
|
||||
href = "#",
|
||||
size,
|
||||
weight,
|
||||
className = "",
|
||||
}) => {
|
||||
// Links
|
||||
if (tag === "a") {
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
className={text({
|
||||
className: className,
|
||||
size: size ?? styleLike ?? "p",
|
||||
weight: weight ?? styleLike ?? "p",
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
// Other tags
|
||||
const styleTag: Tag = tag === "span" ? "p" : tag;
|
||||
return React.createElement(
|
||||
tag,
|
||||
{
|
||||
className: text({
|
||||
className: className,
|
||||
size: size ?? styleLike ?? styleTag,
|
||||
weight: weight ?? styleLike ?? styleTag,
|
||||
}),
|
||||
},
|
||||
children
|
||||
);
|
||||
};
|
||||
|
||||
export default Text;
|
||||
@@ -1,35 +1,35 @@
|
||||
import Text from "@components/Text";
|
||||
import { cva } from "class-variance-authority";
|
||||
import { typo } from "@styles/typography";
|
||||
import { cva, cx } from "class-variance-authority";
|
||||
import type { ZodString } from "zod";
|
||||
|
||||
const input = cva([
|
||||
typo({ tag: "p" }),
|
||||
"appearance-none",
|
||||
"w-full rounded border-[6px] py-2 px-3 md:py-3 md:px-4 leading-tight text-fg border-fg placeholder-fg/50 bg-fg/20",
|
||||
"w-full rounded border-[6px] py-2 px-3 md:py-3 md:px-4 leading-tight border-fg placeholder-fg/50 bg-fg/20",
|
||||
"focus:outline-none",
|
||||
]);
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
validator: ZodString;
|
||||
validator?: ZodString;
|
||||
}
|
||||
|
||||
const TextInput: React.FC<
|
||||
export const TextInput: React.FC<
|
||||
Props &
|
||||
React.DetailedHTMLProps<
|
||||
React.InputHTMLAttributes<HTMLInputElement>,
|
||||
HTMLInputElement
|
||||
>
|
||||
> = ({ label, validator, name, type = "text", ...inputProps }) => {
|
||||
> = ({ label, name, type = "text", ...inputProps }) => {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<label htmlFor={name} className="mb-1 block text-fg">
|
||||
<Text weight="bold">{label}</Text>
|
||||
<label
|
||||
htmlFor={name}
|
||||
className={cx(typo({ size: "base" }), "mb-1 block text-fg")}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
<Text size="h5">
|
||||
<input name={name} type={type} className={input()} {...inputProps} />
|
||||
</Text>
|
||||
<input name={name} type={type} className={input()} {...inputProps} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TextInput;
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import Link from "next/link";
|
||||
import { IoClose } from "react-icons/io5";
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const AuthLayout: React.FC<Props> = ({ children }) => {
|
||||
return (
|
||||
<Link
|
||||
href="/"
|
||||
className="relative flex min-h-screen flex-col items-center justify-center"
|
||||
>
|
||||
<IoClose
|
||||
size="2rem"
|
||||
className="absolute top-0 left-0 m-4 rounded-full hover:bg-fg/10 focus-visible:outline focus-visible:outline-8 focus-visible:outline-offset-2 focus-visible:outline-fg"
|
||||
/>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default AuthLayout;
|
||||
28
src/components/layouts/DiscoverLayout.tsx
Normal file
28
src/components/layouts/DiscoverLayout.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { MainLayout } from "@components/layouts";
|
||||
import { TextInput } from "@components/TextInput";
|
||||
import { typo } from "@styles/typography";
|
||||
import type { Children } from "@utils/types/props";
|
||||
import Link from "next/link";
|
||||
|
||||
export const DiscoverLayout: React.FC<Children> = ({ children }) => {
|
||||
return (
|
||||
<MainLayout>
|
||||
<header>
|
||||
<nav className="mb-4 flex flex-row gap-x-4">
|
||||
<Link className="hover:text-tertiary" href="/discover/proposals">
|
||||
Proposals
|
||||
</Link>
|
||||
<Link className="hover:text-tertiary" href="/discover/revisions">
|
||||
Revisions
|
||||
</Link>
|
||||
<Link className="hover:text-tertiary" href="/discover/archive">
|
||||
Archive
|
||||
</Link>
|
||||
</nav>
|
||||
</header>
|
||||
<h1 className={typo({ tag: "h1" })}>Discover</h1>
|
||||
<TextInput label="Search" />
|
||||
{children}
|
||||
</MainLayout>
|
||||
);
|
||||
};
|
||||
@@ -1,18 +1,13 @@
|
||||
import HomeFooter from "@components/HomeFooter";
|
||||
import HomeHeader from "@components/HomeHeader";
|
||||
import { Footer } from "@components/Footer";
|
||||
import { Header } from "@components/Header";
|
||||
import type { Children } from "@utils/types/props";
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const HomeLayout: React.FC<Props> = ({ children }) => {
|
||||
export const HomeLayout: React.FC<Children> = ({ children }) => {
|
||||
return (
|
||||
<>
|
||||
<HomeHeader />
|
||||
<Header />
|
||||
{children}
|
||||
<HomeFooter />
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default HomeLayout;
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
import type { Children } from "@utils/types/props";
|
||||
import Link from "next/link";
|
||||
import { IoClose } from "react-icons/io5";
|
||||
|
||||
const InfoLayout: React.FC<Props> = ({ children }) => {
|
||||
return <>{children}</>;
|
||||
export const InfoLayout: React.FC<Children> = ({ children }) => {
|
||||
return (
|
||||
<>
|
||||
<Link
|
||||
href="/"
|
||||
className="relative flex min-h-screen flex-col items-center justify-center"
|
||||
>
|
||||
<IoClose
|
||||
size="2rem"
|
||||
className="absolute top-0 left-0 m-4 rounded-full hover:bg-fg/10 focus-visible:outline focus-visible:outline-8 focus-visible:outline-offset-2 focus-visible:outline-fg"
|
||||
/>
|
||||
</Link>
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default InfoLayout;
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import { Murecho } from "@next/font/google";
|
||||
import { useRouter } from "next/router";
|
||||
import AuthLayout from "@components/layouts/AuthLayout";
|
||||
import HomeLayout from "@components/layouts/HomeLayout";
|
||||
import InfoLayout from "@components/layouts/InfoLayout";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const murecho = Murecho({
|
||||
subsets: ["latin"],
|
||||
weight: ["400", "500", "600", "700"],
|
||||
display: "swap",
|
||||
variable: "--font-murecho",
|
||||
});
|
||||
|
||||
type LayoutWrapper = "none" | "home" | "auth" | "info";
|
||||
const layoutWrappers: Record<
|
||||
LayoutWrapper,
|
||||
React.FC<{ children: React.ReactNode }>
|
||||
> = {
|
||||
none: ({ children }: { children: React.ReactNode }) => <>{children}</>,
|
||||
home: HomeLayout,
|
||||
info: InfoLayout,
|
||||
auth: AuthLayout,
|
||||
};
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
layout?: LayoutWrapper;
|
||||
}
|
||||
|
||||
const Layout: React.FC<Props> = ({ layout = "none", children }) => {
|
||||
const ContextualLayout = layoutWrappers[layout];
|
||||
|
||||
return (
|
||||
<main className={`${murecho.variable} font-sans`}>
|
||||
<ContextualLayout>{children}</ContextualLayout>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
28
src/components/layouts/MainLayout.tsx
Normal file
28
src/components/layouts/MainLayout.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { Children } from "@utils/types/props";
|
||||
import Link from "next/link";
|
||||
|
||||
const TempHeader = () => (
|
||||
<header>
|
||||
<nav className="flex flex-row gap-x-4">
|
||||
<Link className="hover:text-tertiary" href="/projects">
|
||||
Projects
|
||||
</Link>
|
||||
<Link className="hover:text-tertiary" href="/discover">
|
||||
Discover
|
||||
</Link>
|
||||
<Link className="hover:text-tertiary" href="/profile">
|
||||
Profile
|
||||
</Link>
|
||||
</nav>
|
||||
</header>
|
||||
);
|
||||
|
||||
export const MainLayout: React.FC<Children> = ({ children }) => {
|
||||
return (
|
||||
<>
|
||||
<TempHeader />
|
||||
{/* <SidePanel/> */}
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
};
|
||||
13
src/components/layouts/RootLayout.tsx
Normal file
13
src/components/layouts/RootLayout.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Murecho } from "@next/font/google";
|
||||
import type { Children } from "@utils/types/props";
|
||||
|
||||
const murecho = Murecho({
|
||||
subsets: ["latin"],
|
||||
weight: ["400", "500", "600", "700"],
|
||||
display: "swap",
|
||||
variable: "--font-murecho",
|
||||
});
|
||||
|
||||
export const RootLayout: React.FC<Children> = ({ children }) => {
|
||||
return <main className={`${murecho.variable} font-sans`}>{children}</main>;
|
||||
};
|
||||
5
src/components/layouts/index.ts
Normal file
5
src/components/layouts/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from "./RootLayout";
|
||||
export * from "./HomeLayout";
|
||||
export * from "./MainLayout";
|
||||
export * from "./InfoLayout";
|
||||
export * from "./DiscoverLayout";
|
||||
@@ -1,8 +1,8 @@
|
||||
import Layout from "@components/layouts/Layout";
|
||||
import { InfoLayout } from "@components/layouts";
|
||||
import { type NextPage } from "next";
|
||||
|
||||
const Error: NextPage = () => {
|
||||
return <Layout layout="info">Error</Layout>;
|
||||
return <InfoLayout>Error</InfoLayout>;
|
||||
};
|
||||
|
||||
export default Error;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Button } from "@components/Button";
|
||||
import TextInput from "@components/TextInput";
|
||||
import Text from "@components/Text";
|
||||
import { TextInput } from "@components/TextInput";
|
||||
import type {
|
||||
GetServerSidePropsContext,
|
||||
InferGetServerSidePropsType,
|
||||
@@ -10,8 +9,11 @@ import Image from "next/image";
|
||||
import Divider from "@components/Divider";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect } from "react";
|
||||
import Layout from "@components/layouts/Layout";
|
||||
import { InfoLayout } from "@components/layouts";
|
||||
import { SignInFields } from "@constants/auth";
|
||||
import { typo } from "@styles/typography";
|
||||
import { cx } from "class-variance-authority";
|
||||
import Link from "next/link";
|
||||
|
||||
const SignIn = ({
|
||||
csrfToken,
|
||||
@@ -35,10 +37,15 @@ const SignIn = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout layout="auth">
|
||||
<Text styleLike="h3" className="my-6 mx-10 text-center text-primary">
|
||||
<InfoLayout>
|
||||
<h1
|
||||
className={cx(
|
||||
typo({ tag: "h3" }),
|
||||
"my-6 mx-10 text-center text-primary"
|
||||
)}
|
||||
>
|
||||
Welcome back. Sign in.
|
||||
</Text>
|
||||
</h1>
|
||||
<form
|
||||
onSubmit={handleAuth}
|
||||
className="flex w-full max-w-lg flex-col items-center justify-center gap-y-2.5 px-4"
|
||||
@@ -51,17 +58,18 @@ const SignIn = ({
|
||||
Sign In
|
||||
</Button>
|
||||
</form>
|
||||
<Text className="mt-8 mb-12">
|
||||
<p className={cx(typo({ tag: "p" }), "mt-8 mb-12")}>
|
||||
{"Don't have an account? "}
|
||||
<Text
|
||||
tag="a"
|
||||
href="/auth/sign-up"
|
||||
weight="semibold"
|
||||
className="text-tertiary hover:text-tertiary-600"
|
||||
<Link
|
||||
href="/auth/sign-in"
|
||||
className={cx(
|
||||
typo({ size: "base" }),
|
||||
"font-semibold text-tertiary hover:text-tertiary-600"
|
||||
)}
|
||||
>
|
||||
Sign up
|
||||
</Text>
|
||||
</Text>
|
||||
</Link>
|
||||
</p>
|
||||
<div className="w-full min-w-max max-w-md px-12">
|
||||
<Divider>or sign in with</Divider>
|
||||
</div>
|
||||
@@ -70,7 +78,7 @@ const SignIn = ({
|
||||
<AuthWith name="Google" />
|
||||
<AuthWith name="Apple" />
|
||||
</div>
|
||||
</Layout>
|
||||
</InfoLayout>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Button } from "@components/Button";
|
||||
import TextInput from "@components/TextInput";
|
||||
import Text from "@components/Text";
|
||||
import { TextInput } from "@components/TextInput";
|
||||
import type {
|
||||
GetServerSidePropsContext,
|
||||
InferGetServerSidePropsType,
|
||||
@@ -11,8 +10,10 @@ import Divider from "@components/Divider";
|
||||
import { useRouter } from "next/router";
|
||||
import Link from "next/link";
|
||||
import { useEffect } from "react";
|
||||
import Layout from "@components/layouts/Layout";
|
||||
import { InfoLayout } from "@components/layouts";
|
||||
import { SignUpFields } from "@constants/auth";
|
||||
import { cx } from "class-variance-authority";
|
||||
import { typo } from "@styles/typography";
|
||||
|
||||
const SignUp = ({
|
||||
csrfToken,
|
||||
@@ -33,10 +34,15 @@ const SignUp = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout layout="auth">
|
||||
<Text styleLike="h3" className="my-6 mx-10 text-center text-primary">
|
||||
<InfoLayout>
|
||||
<h1
|
||||
className={cx(
|
||||
typo({ tag: "h3" }),
|
||||
"my-6 mx-10 text-center text-primary"
|
||||
)}
|
||||
>
|
||||
Sign up. Get Connected.
|
||||
</Text>
|
||||
</h1>
|
||||
<form
|
||||
onSubmit={handleEmailAuth}
|
||||
className="flex w-full max-w-lg flex-col items-center justify-center gap-y-2.5 px-4"
|
||||
@@ -49,17 +55,18 @@ const SignUp = ({
|
||||
Sign Up
|
||||
</Button>
|
||||
</form>
|
||||
<Text className="mt-8 mb-12">
|
||||
<p className={cx(typo({ tag: "p" }), "mt-8 mb-12")}>
|
||||
Already have an account?{" "}
|
||||
<Text
|
||||
tag="a"
|
||||
<Link
|
||||
href="/auth/sign-in"
|
||||
weight="semibold"
|
||||
className="text-tertiary hover:text-tertiary-600"
|
||||
className={cx(
|
||||
typo({ size: "base" }),
|
||||
"font-semibold text-tertiary hover:text-tertiary-600"
|
||||
)}
|
||||
>
|
||||
Sign in
|
||||
</Text>
|
||||
</Text>
|
||||
</Link>
|
||||
</p>
|
||||
<div className="w-full min-w-max max-w-md px-12">
|
||||
<Divider>or sign up with</Divider>
|
||||
</div>
|
||||
@@ -68,7 +75,7 @@ const SignUp = ({
|
||||
<AuthWith name="Google" />
|
||||
<AuthWith name="Apple" />
|
||||
</div>
|
||||
</Layout>
|
||||
</InfoLayout>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import Layout from "@components/layouts/Layout";
|
||||
import { InfoLayout } from "@components/layouts";
|
||||
import { type NextPage } from "next";
|
||||
|
||||
const VerifyRequest: NextPage = () => {
|
||||
return <Layout layout="info">Verify Request</Layout>;
|
||||
return <InfoLayout>Verify Request</InfoLayout>;
|
||||
};
|
||||
|
||||
export default VerifyRequest;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { type Session } from "next-auth";
|
||||
import { SessionProvider } from "next-auth/react";
|
||||
import { SSRProvider } from "react-aria";
|
||||
import Head from "next/head";
|
||||
import { RootLayout } from "@components/layouts";
|
||||
import { api } from "@utils/api";
|
||||
import "@styles/globals.css";
|
||||
|
||||
@@ -19,7 +20,9 @@ const ParallelApp: AppType<{ session: Session | null }> = ({
|
||||
</Head>
|
||||
<SSRProvider>
|
||||
<SessionProvider session={session}>
|
||||
<Component {...pageProps} />
|
||||
<RootLayout>
|
||||
<Component {...pageProps} />
|
||||
</RootLayout>
|
||||
</SessionProvider>
|
||||
</SSRProvider>
|
||||
</>
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import Layout from "@components/layouts/Layout";
|
||||
import Text from "@components/Text";
|
||||
import type { NextPage } from "next";
|
||||
|
||||
const About: NextPage = () => {
|
||||
return (
|
||||
<Layout layout="home">
|
||||
<Text tag="h1">About</Text>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default About;
|
||||
13
src/pages/discover/archive/index.tsx
Normal file
13
src/pages/discover/archive/index.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { NextPage } from "next";
|
||||
import { DiscoverLayout } from "@components/layouts";
|
||||
import { typo } from "@styles/typography";
|
||||
|
||||
const Archive: NextPage = () => {
|
||||
return (
|
||||
<DiscoverLayout>
|
||||
<h2 className={typo({ tag: "h2" })}>Archive</h2>
|
||||
</DiscoverLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Archive;
|
||||
20
src/pages/discover/index.tsx
Normal file
20
src/pages/discover/index.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { DiscoverLayout } from "@components/layouts";
|
||||
import type {
|
||||
GetServerSidePropsContext,
|
||||
InferGetServerSidePropsType,
|
||||
NextPage,
|
||||
} from "next";
|
||||
|
||||
const Discover: NextPage<
|
||||
InferGetServerSidePropsType<typeof getServerSideProps>
|
||||
> = ({ query }) => {
|
||||
return <DiscoverLayout />;
|
||||
};
|
||||
|
||||
export default Discover;
|
||||
|
||||
export function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
const query = context.query;
|
||||
// Call a backend endpoint and return that list
|
||||
return { props: { query } };
|
||||
}
|
||||
13
src/pages/discover/proposals/index.tsx
Normal file
13
src/pages/discover/proposals/index.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { NextPage } from "next";
|
||||
import { DiscoverLayout } from "@components/layouts";
|
||||
import { typo } from "@styles/typography";
|
||||
|
||||
const Proposals: NextPage = () => {
|
||||
return (
|
||||
<DiscoverLayout>
|
||||
<h2 className={typo({ tag: "h2" })}>Proposals</h2>
|
||||
</DiscoverLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Proposals;
|
||||
13
src/pages/discover/revisions/index.tsx
Normal file
13
src/pages/discover/revisions/index.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { NextPage } from "next";
|
||||
import { DiscoverLayout } from "@components/layouts";
|
||||
import { typo } from "@styles/typography";
|
||||
|
||||
const Revisions: NextPage = () => {
|
||||
return (
|
||||
<DiscoverLayout>
|
||||
<h2 className={typo({ tag: "h2" })}>Revisions</h2>
|
||||
</DiscoverLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Revisions;
|
||||
@@ -6,14 +6,15 @@ import type {
|
||||
import Image from "next/image";
|
||||
import { Button } from "@components/Button";
|
||||
import Divider from "@components/Divider";
|
||||
import Text from "@components/Text";
|
||||
import Layout from "@components/layouts/Layout";
|
||||
import { getServerAuthSession } from "@server/auth";
|
||||
import { signIn } from "next-auth/react";
|
||||
import { HomeLayout } from "@components/layouts";
|
||||
import { cx } from "class-variance-authority";
|
||||
import { typo } from "@styles/typography";
|
||||
|
||||
const Home: NextPage = () => {
|
||||
return (
|
||||
<Layout layout="home">
|
||||
<HomeLayout>
|
||||
<Hero />
|
||||
<Divider />
|
||||
<About />
|
||||
@@ -21,7 +22,7 @@ const Home: NextPage = () => {
|
||||
<Premium />
|
||||
<Divider />
|
||||
<CTA />
|
||||
</Layout>
|
||||
</HomeLayout>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -47,14 +48,19 @@ const Hero: React.FC = () => {
|
||||
return (
|
||||
<section className="custom-home-bg">
|
||||
<div className="container">
|
||||
<Text tag="h1" className="leading-tight text-primary drop-shadow-blur">
|
||||
<h1
|
||||
className={cx(
|
||||
typo({ tag: "h1" }),
|
||||
"leading-tight text-primary drop-shadow-blur"
|
||||
)}
|
||||
>
|
||||
Collaborate with experts.
|
||||
<br />
|
||||
Educate the world.
|
||||
</Text>
|
||||
<Text size="h3" weight="bold" className="mt-6 mb-16 drop-shadow-blur">
|
||||
Create educational content.
|
||||
</h1>
|
||||
<h3 className={cx(typo({ tag: "h3" }), "mt-6 mb-16 drop-shadow-blur")}>
|
||||
Get connected with Parallel
|
||||
</Text>
|
||||
</h3>
|
||||
<Button onClick={() => void signIn()}>Get Started</Button>
|
||||
</div>
|
||||
</section>
|
||||
@@ -64,25 +70,33 @@ const Hero: React.FC = () => {
|
||||
const About: React.FC = () => {
|
||||
return (
|
||||
<section className="container">
|
||||
<Text tag="h2" className="mb-5 text-center text-primary">
|
||||
<h2 className={cx(typo({ tag: "h2" }), "mb-5 text-center text-primary")}>
|
||||
About Parallel
|
||||
</Text>
|
||||
<Text className="mx-auto mb-24 max-w-6xl text-center">
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit Ut et massa mi.
|
||||
Aliquam in hendrerit urna. Pellentesque sit amet sapien fringilla,
|
||||
mattis ligula consectetur, ultrices mauris. Maecenas vitae mattis
|
||||
tellus.
|
||||
</Text>
|
||||
</h2>
|
||||
<p
|
||||
className={cx(
|
||||
typo({ tag: "p" }),
|
||||
"mx-auto mb-24 max-w-6xl text-center"
|
||||
)}
|
||||
>
|
||||
Parallel{"'"}s mission is to address the disparity between the number of
|
||||
educators who can produce high-quality educational content and the
|
||||
number of content creators who have the expertise to tackle complex
|
||||
subject matter. We connect content creators with educators, enabling
|
||||
them to collaborate and produce exceptional educational content. Users
|
||||
can assemble teams, work together on projects, and then publish a final
|
||||
piece to platforms like YouTube.
|
||||
</p>
|
||||
<div className="grid grid-cols-1 gap-x-24 gap-y-9 md:grid-cols-[1fr_2fr_1fr] md:items-center">
|
||||
<div className="md:col-start-2">
|
||||
<Text tag="h3" styleLike="h4" className="mb-3">
|
||||
For Educators
|
||||
</Text>
|
||||
<Text>
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit Ut et massa
|
||||
mi. Aliquam in hendrerit urna. Pellentesque sit amet sapien
|
||||
fringilla, mattis ligula consectetur, ultrices mauris.
|
||||
</Text>
|
||||
<h3 className={cx(typo({ tag: "h4" }), "mb-3")}>For Educators</h3>
|
||||
<p className={typo({ tag: "p" })}>
|
||||
Parallel provides educators with a platform to share their expertise
|
||||
with a wider audience by collaborating with content creators. By
|
||||
working together, educators can produce engaging and informative
|
||||
videos that are accessible to students everywhere, without investing
|
||||
too much time in video production.
|
||||
</p>
|
||||
</div>
|
||||
<Image
|
||||
priority
|
||||
@@ -101,14 +115,16 @@ const About: React.FC = () => {
|
||||
className="w-24 justify-self-end md:w-auto"
|
||||
/>
|
||||
<div className="md:col-start-2">
|
||||
<Text tag="h3" styleLike="h4" className="mb-3">
|
||||
<h3 className={cx(typo({ tag: "h4" }), "mb-3")}>
|
||||
For Content Creators
|
||||
</Text>
|
||||
<Text>
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit Ut et massa
|
||||
mi. Aliquam in hendrerit urna. Pellentesque sit amet sapien
|
||||
fringilla, mattis ligula consectetur, ultrices mauris.
|
||||
</Text>
|
||||
</h3>
|
||||
<p className={typo({ tag: "p" })}>
|
||||
Parallel empowers content creators to produce high-quality
|
||||
educational content that they may not have had the expertise to
|
||||
tackle on their own. By collaborating with educators, creators can
|
||||
explore new topics and create more comprehensive videos that engage
|
||||
and inform their audience.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -118,20 +134,21 @@ const About: React.FC = () => {
|
||||
const Premium: React.FC = () => {
|
||||
return (
|
||||
<section className="container text-center">
|
||||
<Text tag="h2" className="text-primary">
|
||||
<h2 className={cx(typo({ tag: "h2" }), "text-primary")}>
|
||||
Parallel Premium
|
||||
</Text>
|
||||
<Text className="my-5">
|
||||
</h2>
|
||||
<p className={cx(typo({ tag: "p" }), "my-5")}>
|
||||
Using Parallel is completely free, but you can enjoy exclusive features
|
||||
and support the platform with our premium version.
|
||||
</Text>
|
||||
<a href="#">
|
||||
<Text
|
||||
weight="semibold"
|
||||
className="text-tertiary hover:text-tertiary-600"
|
||||
>
|
||||
Learn more about Premium
|
||||
</Text>
|
||||
</p>
|
||||
<a
|
||||
href="#"
|
||||
className={cx(
|
||||
typo({ tag: "p" }),
|
||||
"font-semibold text-tertiary hover:text-tertiary-600"
|
||||
)}
|
||||
>
|
||||
Learn more about Premium
|
||||
</a>
|
||||
</section>
|
||||
);
|
||||
|
||||
55
src/pages/profile/index.tsx
Normal file
55
src/pages/profile/index.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Button } from "@components/Button";
|
||||
import { MainLayout } from "@components/layouts";
|
||||
import { getServerAuthSession } from "@server/auth";
|
||||
import { typo } from "@styles/typography";
|
||||
import type { GetServerSideProps, GetServerSidePropsContext } from "next";
|
||||
import { type NextPage } from "next";
|
||||
import type { Session } from "next-auth";
|
||||
import { signOut, useSession } from "next-auth/react";
|
||||
|
||||
interface Props {
|
||||
session: Session;
|
||||
}
|
||||
|
||||
const Profile: NextPage<Props> = ({ session }) => {
|
||||
const { data, status } = useSession();
|
||||
return (
|
||||
<MainLayout>
|
||||
<h1 className={typo({ tag: "h1" })}>Profile</h1>
|
||||
<p className={typo({ tag: "p" })}>{status}</p>
|
||||
<p className={typo({ tag: "p" })}>
|
||||
(Server side) Signed in as {session?.user.name}
|
||||
</p>
|
||||
<p className={typo({ tag: "p" })}>
|
||||
(Client side) Signed in as {data?.user.name}
|
||||
</p>
|
||||
|
||||
<Button
|
||||
onClick={() =>
|
||||
void signOut({
|
||||
callbackUrl: "/",
|
||||
redirect: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
Sign Out
|
||||
</Button>
|
||||
</MainLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<Props> = async (
|
||||
ctx: GetServerSidePropsContext
|
||||
) => {
|
||||
const session = await getServerAuthSession(ctx);
|
||||
|
||||
if (session === null || session.user === null || session.user.id === null) {
|
||||
return {
|
||||
redirect: { destination: "/api/auth/signin", permanent: false },
|
||||
};
|
||||
}
|
||||
|
||||
return { props: { session } };
|
||||
};
|
||||
|
||||
export default Profile;
|
||||
@@ -1,50 +1,13 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { getServerAuthSession } from "@server/auth";
|
||||
import type {
|
||||
GetServerSideProps,
|
||||
GetServerSidePropsContext,
|
||||
InferGetServerSidePropsType,
|
||||
NextPage,
|
||||
} from "next";
|
||||
import Text from "@components/Text";
|
||||
import { Button } from "@components/Button";
|
||||
import { signOut } from "next-auth/react";
|
||||
import Layout from "@components/layouts/Layout";
|
||||
import type { NextPage } from "next";
|
||||
import { MainLayout } from "@components/layouts";
|
||||
import { typo } from "@styles/typography";
|
||||
|
||||
const Projects: NextPage = ({
|
||||
session,
|
||||
}: InferGetServerSidePropsType<typeof getServerSideProps>) => {
|
||||
const Projects: NextPage = () => {
|
||||
return (
|
||||
<Layout>
|
||||
<Text tag="h1">Projects</Text>
|
||||
<Text>Signed in as {session?.user?.id}</Text>
|
||||
|
||||
<Button
|
||||
onClick={() =>
|
||||
void signOut({
|
||||
callbackUrl: "/",
|
||||
redirect: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
Sign Out
|
||||
</Button>
|
||||
</Layout>
|
||||
<MainLayout>
|
||||
<h1 className={typo({ tag: "h1" })}>Projects</h1>
|
||||
</MainLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async (
|
||||
ctx: GetServerSidePropsContext
|
||||
) => {
|
||||
const session = await getServerAuthSession(ctx);
|
||||
|
||||
if (!session) {
|
||||
return {
|
||||
redirect: { destination: "/api/auth/signin", permanent: false },
|
||||
};
|
||||
}
|
||||
|
||||
return { props: { session } };
|
||||
};
|
||||
|
||||
export default Projects;
|
||||
|
||||
26
src/styles/typography.ts
Normal file
26
src/styles/typography.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { cva, cx } from "class-variance-authority";
|
||||
|
||||
const responsiveText = {
|
||||
base: "text-sm md:text-base 2xl:text-lg",
|
||||
lg: "text-sm md:text-base 2xl:text-lg",
|
||||
xl: "text-base md:text-lg 2xl:text-xl",
|
||||
"2xl": "text-lg md:text-xl 2xl:text-2xl",
|
||||
"3xl": "text-xl md:text-2xl 2xl:text-3xl",
|
||||
"4xl": "text-2xl md:text-3xl 2xl:text-4xl",
|
||||
"5xl": "text-3xl md:text-4xl 2xl:text-5xl",
|
||||
};
|
||||
|
||||
export const typo = cva("", {
|
||||
variants: {
|
||||
tag: {
|
||||
p: cx("font-normal", responsiveText["lg"]),
|
||||
h1: cx("font-bold", responsiveText["5xl"]),
|
||||
h2: cx("font-bold", responsiveText["4xl"]),
|
||||
h3: cx("font-bold", responsiveText["3xl"]),
|
||||
h4: cx("font-bold", responsiveText["2xl"]),
|
||||
h5: cx("font-semibold", responsiveText["xl"]),
|
||||
h6: cx("font-semibold", responsiveText["lg"]),
|
||||
},
|
||||
size: responsiveText,
|
||||
},
|
||||
});
|
||||
3
src/utils/types/props.ts
Normal file
3
src/utils/types/props.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface Children {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
Reference in New Issue
Block a user