🎨 Add header

This commit is contained in:
adam.watkins
2023-04-07 08:43:48 +03:00
parent 34efb140d5
commit d491038d40
11 changed files with 430 additions and 20 deletions

1
public/logo-white.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 205 KiB

View File

@@ -2,11 +2,12 @@ import React from "react";
interface DottedGridBackgroundProps { interface DottedGridBackgroundProps {
children: React.ReactNode; children: React.ReactNode;
className?: string
} }
const DottedGridBackground = ({ children }: DottedGridBackgroundProps) => { const DottedGridBackground = ({ children, className }: DottedGridBackgroundProps) => {
return ( return (
<div <div
className="flex flex-col justify-center items-center bg-gradient-to-b from-gray-100 to-transparent w-screen h-screen background" className={`${className ? className + " " : ""} background`}
> >
{children} {children}
</div> </div>

92
src/components/Header.tsx Normal file
View File

@@ -0,0 +1,92 @@
// import { AnimatePresence, motion } from "framer-motion";
import Link from "next/link";
import { useRouter } from "next/router";
import {
FaAngleDown,
FaGithub,
FaHome,
FaLink,
FaSignOutAlt,
} from "react-icons/fa";
import Image from "next/image";
// import { Dropdown, DropdownItem } from "@/ui/dropdown";
// import Loader from "@/ui/loader";
//
// import { useAuth } from "@/hooks/useAuth";
const Header: React.FC = () => {
// const { signOut, session, status } = useAuth();
const router = useRouter();
// const authenticated = status == "authenticated" && (
// <Dropdown
// title={session?.user?.name || ""}
// icon={<FaAngleDown className="h-5 text-inherit" />}
// loader={false}
// >
// <h1 className="border-white/5 border-b-2 font-bold px-3 pb-1 text-md">
// {session?.user?.name}
// </h1>
//
// <DropdownItem
// icon={<FaSignOutAlt className="h-4 text-inherit text-white" />}
// onClick={signOut}
// >
// Sign Out
// </DropdownItem>
// {router.route != "/" && (
// <DropdownItem icon={<FaHome className="h-4 text-inherit" />}>
// <Link href="/">Home</Link>
// </DropdownItem>
// )}
// <DropdownItem icon={<FaLink className="h-4 text-inherit" />}>
// <a
// href="https://github.com/awtkns/confetti/issues/new"
// target="_blank"
// rel="noreferrer"
// >
// Report a bug
// </a>
// </DropdownItem>
// </Dropdown>
// );
// const loading = status == "loading" && <Loader />;
//
// const unauthenticated =
// status == "unauthenticated" && router.route != "/auth" ? (
// <Link href="/auth">Sign In</Link>
// ) : (
// <Link href="/">Home</Link>
// );
const github = (
<a
href="https://github.com/reworkd/AgentGPT"
className="right-0 ml-0 block block text-white hover:text-yellow-500"
>
<span className="sr-only">Confetti on GitHub</span>
<FaGithub size="25" />
</a>
);
return (
<header className="z-50 w-full p-2 relative flex flex-row items-center justify-between align-middle">
<Image src="logo-white.svg" alt="yes" width={30} height={0} />
{/*<AnimatePresence>*/}
{/* <motion.div*/}
{/* initial={{ opacity: 0 }}*/}
{/* animate={{ opacity: 1 }}*/}
{/* transition={{ duration: 1, type: "spring" }}*/}
{/* className="ml-auto pr-4 text-lg text-white hover:text-yellow-500"*/}
{/* >*/}
{/* {authenticated || loading || unauthenticated}*/}
{/* </motion.div>*/}
{/*</AnimatePresence>*/}
{github}
</header>
);
};
export default Header;

33
src/layout/default.tsx Normal file
View File

@@ -0,0 +1,33 @@
// import Footer from "../components/Footer";
// import Header from "../components/Header";
import { ReactNode } from "react";
import Head from "next/head";
import DottedGridBackground from "../components/DottedGridBackground";
import Header from "../components/Header";
interface LayoutProps {
children: ReactNode;
}
const DefaultLayout = (props: LayoutProps) => {
return (
<div
className="flex min-h-screen flex-col min-h-screen bg-gradient-to-b from-[#2B2B2B] to-[#1F1F1F]"
>
<Head>
<title>Agent-GPT</title>
<meta name="description" content="Agent-GPT b Reworkd.ai"/>
<link rel="icon" href="/favicon.ico"/>
</Head>
<DottedGridBackground>
<Header />
<main className="flex flex-col justify-center items-center w-screen h-screen ">
{props.children}
</main>
</DottedGridBackground>
{/*<Footer />*/}
</div>
);
};
export default DefaultLayout;

View File

@@ -2,25 +2,22 @@ import { type NextPage } from "next";
import Head from "next/head"; import Head from "next/head";
import DottedGridBackground from "../components/DottedGridBackground"; import DottedGridBackground from "../components/DottedGridBackground";
import Badge from "../components/Badge"; import Badge from "../components/Badge";
import Input from "../ui/input";
import {useState} from "react";
import DefaultLayout from "../layout/default";
const Home: NextPage = () => { const Home: NextPage = () => {
const input = useState("")
return ( return (
<> <DefaultLayout>
<Head>
<title>Agent-GPT</title>
<meta name="description" content="Agent-GPT b Reworkd.ai" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className="min-h-screen bg-gradient-to-b from-[#2B2B2B] to-[#1F1F1F]">
<DottedGridBackground>
<div id="title" className="flex gap-4 items-center"> <div id="title" className="flex gap-4 items-center">
<div className="font-bold text-4xl text-[#C0C0C0]">AgentGPT</div> <div className="font-bold text-4xl text-[#C0C0C0]">AgentGPT</div>
<Badge>Beta 🚀</Badge> <Badge>Beta 🚀</Badge>
</div> </div>
</DottedGridBackground> <Input model={input}/>
</main> </DefaultLayout>
</>
); );
}; };

55
src/ui/button.tsx Normal file
View File

@@ -0,0 +1,55 @@
import type { ForwardedRef } from "react";
import { forwardRef, useState } from "react";
import Loader from "./loader";
export interface ButtonProps {
type?: "button" | "submit" | "reset";
className?: string;
icon?: React.ReactNode;
children?: React.ReactNode;
loader?: boolean;
disabled?: boolean;
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => Promise<void> | void;
}
const Button = forwardRef(
(props: ButtonProps, ref: ForwardedRef<HTMLButtonElement>) => {
const [loading, setLoading] = useState(false);
const onClick = (e: React.MouseEvent<HTMLButtonElement>) => {
if (props.loader == true) setLoading(true);
try {
Promise.resolve(props.onClick?.(e)).then();
} catch (e) {
setLoading(false);
}
};
return (
<button
ref={ref}
type={props.type}
disabled={loading || props.disabled}
onClick={onClick}
className={
"text-white transition hover:text-yellow-500 " + props.className
}
>
<div className="flex items-center">
{loading ? (
<Loader />
) : (
<>
{props.icon ? <div className="mr-2">{props.icon}</div> : null}
{props.children}
</>
)}
</div>
</button>
);
}
);
Button.displayName = "Button";
export default Button;

50
src/ui/dropdown.tsx Normal file
View File

@@ -0,0 +1,50 @@
import { Menu, Transition } from "@headlessui/react";
import { Fragment } from "react";
import type { ButtonProps } from "./button";
import Button from "./button";
interface DropdownProps extends ButtonProps {
title?: string | undefined;
onClick?: () => void;
}
export const Dropdown = (props: DropdownProps) => {
return (
<Menu as="div" className="right-0 ml-auto">
<Menu.Button as={Button} className={props.className} icon={props.icon}>
<span className="hidden md:flex">{props.title}</span>
</Menu.Button>
<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-2 z-40 mt-2 w-56 origin-top-right divide-y rounded-lg bg-slate-900 p-1 shadow-sm focus:outline-none">
<div className="py-1">{props.children}</div>
</Menu.Items>
</Transition>
</Menu>
);
};
export const DropdownItem = (props: DropdownProps) => {
return (
<Menu.Item>
<div
className={`block cursor-pointer justify-between rounded px-3 py-2 text-sm text-white duration-200 hover:text-yellow-500
${props.className}`}
onClick={props.onClick}
>
<div className="flex items-center">
{props.icon && <div className="mr-3">{props.icon}</div>}
{props.children}
</div>
</div>
</Menu.Item>
);
};

61
src/ui/input.tsx Normal file
View File

@@ -0,0 +1,61 @@
// import Loader from "@/motions/loader";
import type {
Dispatch,
ForwardedRef,
InputHTMLAttributes,
KeyboardEventHandler,
SetStateAction,
} from "react";
import { forwardRef } from "react";
const SHARED_STYLE = "rounded-full ";
const STYLE =
SHARED_STYLE +
" border-gray-300 focus:border-yellow-500 focus:ring-yellow-500 ";
const ERROR_STYLE =
SHARED_STYLE + " border-red-500 focus:border-red-500 focus:ring-red-500 ";
export interface InputProps<T> extends InputHTMLAttributes<HTMLInputElement> {
model: [T, Dispatch<SetStateAction<T>>];
error?: [boolean, Dispatch<SetStateAction<boolean>>];
enterPressed?: () => void;
}
const Input = forwardRef(
(props: InputProps<string>, ref: ForwardedRef<HTMLInputElement>) => {
const { model, error, enterPressed, onKeyDown, className, ...otherProps } =
props;
const [isError, setIsError] = error || [false, () => undefined];
const keyDown: KeyboardEventHandler<HTMLInputElement> = (e) => {
try {
if (e.key === "Enter" && enterPressed) {
e.preventDefault();
enterPressed();
return;
}
if (onKeyDown) onKeyDown(e);
} catch (e) {
setIsError(true);
}
};
return (
<input
ref={ref}
onKeyDown={keyDown}
value={model[0]}
onChange={(e) => {
model[1](e.target.value);
setIsError(false);
}}
className={(isError ? ERROR_STYLE : STYLE) + className}
{...otherProps}
/>
);
}
);
Input.displayName = "input";
export default Input;

23
src/ui/loader.tsx Normal file
View File

@@ -0,0 +1,23 @@
import { Ring } from "@uiball/loaders";
interface LoaderProps {
className?: string;
size?: number;
speed?: number;
lineWeight?: number;
}
const Loader: React.FC<LoaderProps> = ({
className,
size = 16,
speed = 2,
lineWeight = 7,
}) => {
return (
<div className={className}>
<Ring size={size} speed={speed} color="white" lineWeight={lineWeight} />
</div>
);
};
export default Loader;

20
src/ui/popin.tsx Normal file
View File

@@ -0,0 +1,20 @@
import { motion } from "framer-motion";
import type { PropsWithChildren } from "react";
interface MotionProps extends PropsWithChildren {
className?: string;
}
const PopIn = (props: MotionProps) => (
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ duration: 0.5, type: "spring" }}
{...props}
>
{props.children}
</motion.div>
);
PopIn.displayName = "PopIn";
export default PopIn;

77
src/ui/toast.tsx Normal file
View File

@@ -0,0 +1,77 @@
import * as ToastPrimitive from "@radix-ui/react-toast";
import cx from "classnames";
import type { Dispatch, SetStateAction } from "react";
import React from "react";
type Props = {
model: [boolean, Dispatch<SetStateAction<boolean>>];
onAction?: () => void;
title: string;
description?: string;
};
const Toast = (props: Props) => {
const [open, setOpen] = props.model;
return (
<ToastPrimitive.Provider swipeDirection={"right"}>
<ToastPrimitive.Root
open={open}
onOpenChange={setOpen}
className={cx(
"fixed inset-x-4 bottom-4 z-50 w-auto rounded-2xl shadow-lg md:right-4 md:left-auto md:w-full md:max-w-sm",
"bg-slate-900",
"radix-state-open:animate-toast-slide-in-bottom md:radix-state-open:animate-toast-slide-in-right",
"radix-state-closed:animate-toast-hide",
"radix-swipe-direction-right:radix-swipe-end:animate-toast-swipe-out-x",
"radix-swipe-direction-right:translate-x-radix-toast-swipe-move-x",
"radix-swipe-direction-down:radix-swipe-end:animate-toast-swipe-out-y",
"radix-swipe-direction-down:translate-y-radix-toast-swipe-move-y",
"radix-swipe-cancel:translate-x-0 radix-swipe-cancel:duration-200 radix-swipe-cancel:ease-[ease]",
"focus:outline-none focus-visible:ring focus-visible:ring-purple-500 focus-visible:ring-opacity-75"
)}
>
<div className="flex">
<div className="flex w-0 flex-1 items-center py-4 pl-5">
<div className="radix w-full">
<ToastPrimitive.Title className="text-lg font-medium text-white">
{props.title}
</ToastPrimitive.Title>
{props.description && (
<ToastPrimitive.Description className="dark:text-gray-10 text-md mt-1 rounded-md bg-slate-800/50 p-1 text-white">
<pre className="overflow-hidden text-ellipsis">
{props.description}
</pre>
</ToastPrimitive.Description>
)}
</div>
</div>
<div className="mx-4 flex items-center justify-center py-4">
<div className="flex flex-col ">
{props.onAction && (
<ToastPrimitive.Action
altText="copy"
className="text-md flex w-full items-center justify-center rounded-2xl border border-transparent px-3 py-2 font-medium text-yellow-500 hover:bg-white/20 "
onClick={(e) => {
e.preventDefault();
if (props.onAction) props.onAction();
setOpen(false);
}}
>
Copy
</ToastPrimitive.Action>
)}
<ToastPrimitive.Close className="text-md flex w-full items-center justify-center rounded-2xl border border-transparent px-3 py-2 font-medium text-white hover:bg-white/20 ">
Close
</ToastPrimitive.Close>
</div>
</div>
</div>
</ToastPrimitive.Root>
<ToastPrimitive.Viewport />
</ToastPrimitive.Provider>
);
};
export default Toast;