From 735fa9354b951503a5d32850f281df7405a8af28 Mon Sep 17 00:00:00 2001 From: Khairul Hidayat Date: Fri, 23 Feb 2024 19:36:10 +0700 Subject: [PATCH] feat: add homepage & auth --- .env | 3 +- .env.example | 3 +- components/containers/footer.tsx | 35 +++++ components/containers/logo.tsx | 27 ++++ components/containers/navbar.tsx | 85 +++++++++++ components/layout/main-layout.tsx | 18 +++ components/ui/button.tsx | 34 +++-- components/ui/card.tsx | 25 +++ components/ui/divider.tsx | 10 ++ components/ui/form-field.tsx | 34 +++++ components/ui/input.tsx | 74 +++++++-- hooks/useAuth.ts | 7 + hooks/useForm.ts | 4 + lib/utils.ts | 8 + package.json | 5 + pages/auth/+Layout.tsx | 3 + pages/auth/login/+Page.tsx | 61 ++++++++ pages/index/+Layout.tsx | 3 + pages/index/+Page.tsx | 57 ++++++- pages/index/get-started/+Page.tsx | 143 ++++++++++++++++++ pages/index/get-started/+data.ts | 11 ++ .../@slug/components/createfile-dialog.tsx | 4 +- .../project/@slug/components/web-preview.tsx | 3 +- pnpm-lock.yaml | 106 +++++++++++++ renderer/+config.ts | 5 +- renderer/+onRenderClient.tsx | 2 +- renderer/+onRenderHtml.tsx | 6 +- renderer/{layout.tsx => app.tsx} | 12 +- renderer/globals.css | 2 +- renderer/link.tsx | 20 ++- renderer/types.ts | 7 +- server/api/trpc/context.ts | 6 +- server/api/trpc/trpc.ts | 2 +- server/db/schema/user.ts | 3 +- server/index.ts | 5 +- server/lib/jwt.ts | 26 ++++ server/middlewares/auth.ts | 38 +++++ server/routers/_app.ts | 2 + server/routers/auth.ts | 54 +++++++ server/routers/project.ts | 84 ++++++++-- server/types.ts | 11 ++ tailwind.config.ts | 3 + 42 files changed, 981 insertions(+), 70 deletions(-) create mode 100644 components/containers/footer.tsx create mode 100644 components/containers/logo.tsx create mode 100644 components/containers/navbar.tsx create mode 100644 components/layout/main-layout.tsx create mode 100644 components/ui/card.tsx create mode 100644 components/ui/divider.tsx create mode 100644 components/ui/form-field.tsx create mode 100644 hooks/useAuth.ts create mode 100644 pages/auth/+Layout.tsx create mode 100644 pages/auth/login/+Page.tsx create mode 100644 pages/index/+Layout.tsx create mode 100644 pages/index/get-started/+Page.tsx create mode 100644 pages/index/get-started/+data.ts rename renderer/{layout.tsx => app.tsx} (64%) create mode 100644 server/lib/jwt.ts create mode 100644 server/middlewares/auth.ts create mode 100644 server/routers/auth.ts create mode 100644 server/types.ts diff --git a/.env b/.env index 4ce83b9..2bd6ce6 100644 --- a/.env +++ b/.env @@ -1 +1,2 @@ -NEXT_PUBLIC_BASE_URL=http://localhost:3000 +BASE_URL=http://localhost:3000 +JWT_KEY=test123 diff --git a/.env.example b/.env.example index 4ce83b9..2bd6ce6 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,2 @@ -NEXT_PUBLIC_BASE_URL=http://localhost:3000 +BASE_URL=http://localhost:3000 +JWT_KEY=test123 diff --git a/components/containers/footer.tsx b/components/containers/footer.tsx new file mode 100644 index 0000000..b8dc1bd --- /dev/null +++ b/components/containers/footer.tsx @@ -0,0 +1,35 @@ +import Link from "~/renderer/link"; +import Logo from "./logo"; + +const Footer = () => { + return ( + + ); +}; + +export default Footer; diff --git a/components/containers/logo.tsx b/components/containers/logo.tsx new file mode 100644 index 0000000..58b179f --- /dev/null +++ b/components/containers/logo.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import { cn } from "~/lib/utils"; + +type Props = { + size?: "sm" | "md" | "lg"; +}; + +const Logo = ({ size = "md" }: Props) => { + const sizes: Record = { + sm: "text-lg", + md: "text-xl", + lg: "text-2xl", + }; + + return ( +

+ CodeShare +

+ ); +}; + +export default Logo; diff --git a/components/containers/navbar.tsx b/components/containers/navbar.tsx new file mode 100644 index 0000000..27e7bd2 --- /dev/null +++ b/components/containers/navbar.tsx @@ -0,0 +1,85 @@ +import React, { ComponentProps } from "react"; +import { cn } from "~/lib/utils"; +import Link from "~/renderer/link"; +import { Button } from "../ui/button"; +import Logo from "./logo"; +import { useAuth } from "~/hooks/useAuth"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "../ui/dropdown-menu"; +import { FaChevronDown } from "react-icons/fa"; +import trpc from "~/lib/trpc"; + +const Navbar = () => { + const { user } = useAuth(); + const logout = trpc.auth.logout.useMutation({ + onSuccess() { + window.location.reload(); + }, + }); + + return ( + <> +
+ +
+
+ + + + Home + {/* Browse */} + + +
+ {user ? ( + + + + + + logout.mutate()}> + Logout + + + + ) : ( + + )} +
+
+
+ + ); +}; + +const NavMenu = ({ children, className }: ComponentProps<"nav">) => ( + +); + +type NavItemProps = { + href?: string; + children?: React.ReactNode; +}; + +const NavItem = ({ href, children }: NavItemProps) => { + return ( + + {children} + + ); +}; + +export default Navbar; diff --git a/components/layout/main-layout.tsx b/components/layout/main-layout.tsx new file mode 100644 index 0000000..954fb05 --- /dev/null +++ b/components/layout/main-layout.tsx @@ -0,0 +1,18 @@ +import React from "react"; +import Navbar from "../containers/navbar"; + +type LayoutProps = { + children: React.ReactNode; +}; + +const MainLayout = ({ children }: LayoutProps) => { + return ( +
+ + + {children} +
+ ); +}; + +export default MainLayout; diff --git a/components/ui/button.tsx b/components/ui/button.tsx index b2b567e..6ad7650 100644 --- a/components/ui/button.tsx +++ b/components/ui/button.tsx @@ -3,6 +3,7 @@ import { Slot } from "@radix-ui/react-slot"; import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "~/lib/utils"; +import { Loader2 } from "lucide-react"; const buttonVariants = cva( "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-slate-950 dark:focus-visible:ring-slate-300", @@ -14,7 +15,7 @@ const buttonVariants = cva( destructive: "bg-red-500 text-slate-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-slate-50 dark:hover:bg-red-900/90", outline: - "border border-slate-200 bg-white hover:bg-slate-100 hover:text-slate-900 dark:border-slate-800 dark:bg-slate-950 dark:hover:bg-slate-800 dark:hover:text-slate-50", + "border border-slate-200 bg-white hover:bg-slate-100 hover:text-slate-900 dark:border-white/40 dark:bg-transparent dark:hover:bg-slate-800 dark:hover:text-slate-50", secondary: "bg-slate-100 text-slate-900 hover:bg-slate-100/80 dark:bg-slate-800 dark:text-slate-50 dark:hover:bg-slate-800/80", ghost: @@ -39,21 +40,36 @@ export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { asChild?: boolean; + href?: string; + isLoading?: boolean; } const Button = React.forwardRef( - ( - { className, variant, size, type = "button", asChild = false, ...props }, - ref - ) => { - const Comp = asChild ? Slot : "button"; + (props, ref) => { + const { + className, + variant, + size, + type = "button", + asChild = false, + disabled, + isLoading, + children, + ...restProps + } = props; + const Comp = asChild ? Slot : props.href ? "a" : "button"; + return ( + ref={ref as any} + disabled={isLoading || disabled} + {...(restProps as any)} + > + {isLoading ? : null} + {children} + ); } ); diff --git a/components/ui/card.tsx b/components/ui/card.tsx new file mode 100644 index 0000000..07ce1f3 --- /dev/null +++ b/components/ui/card.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import { cn } from "~/lib/utils"; + +type Props = React.ComponentProps<"div">; + +const Card = ({ className, ...props }: Props) => { + return ( +
+ ); +}; + +export const CardTitle = ({ + className, + ...props +}: React.ComponentProps<"p">) => ( +

+); + +export default Card; diff --git a/components/ui/divider.tsx b/components/ui/divider.tsx new file mode 100644 index 0000000..f24d91d --- /dev/null +++ b/components/ui/divider.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import { cn } from "~/lib/utils"; + +type Props = React.ComponentProps<"hr">; + +const Divider = ({ className, ...props }: Props) => { + return


; +}; + +export default Divider; diff --git a/components/ui/form-field.tsx b/components/ui/form-field.tsx new file mode 100644 index 0000000..07c3c07 --- /dev/null +++ b/components/ui/form-field.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import { cn } from "~/lib/utils"; + +export type FormFieldProps = { + id?: string; + label?: string; + error?: string; +}; + +type Props = FormFieldProps & { + children?: React.ReactNode; + className?: string; +}; + +const FormField = ({ id, label, error, children, className }: Props) => { + return ( +
+ {label ? {label} : null} + + {children} + + {error ?

{error}

: null} +
+ ); +}; + +export const FormLabel = ({ + className, + ...props +}: React.ComponentProps<"label">) => ( +