Skip to content

Commit

Permalink
feat: admin layout
Browse files Browse the repository at this point in the history
  • Loading branch information
mrevanzak committed May 20, 2024
1 parent 62b0c90 commit e290b90
Show file tree
Hide file tree
Showing 19 changed files with 475 additions and 33 deletions.
6 changes: 3 additions & 3 deletions apps/web/src/app/(app)/@admin/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { Chat } from "@tanya.in/ui/chat";

export default function HomePage() {
return (
<div className="flex flex-1 justify-center self-center">
<Card className="">
<div className="flex-1 self-center">
<Card className="m-2 mx-auto w-full transition-size has-[[data-started=false]]:min-[450px]:w-96">
<CardHeader>
<CardTitle className="text-center">Admin</CardTitle>
</CardHeader>
Expand All @@ -19,7 +19,7 @@ export default function HomePage() {
</CardContent>
<CardFooter>
<Card className="m-auto shadow-none">
<CardContent className="min-w-96 py-4 text-center">
<CardContent className="py-4 text-center">
/: To choose the topic
</CardContent>
</Card>
Expand Down
19 changes: 12 additions & 7 deletions apps/web/src/app/(app)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import { Footer } from "@/components/footer";
import { Navbar } from "@/components/navbar";
import { SidebarWrapper } from "@/components/sidebar/sidebar";
import { auth } from "@/lib/auth";

export default async function AuthLayout(props: {
user: React.ReactNode;
admin: React.ReactNode;
}) {
const session = await auth();
const isAdmin = session?.user.role === "admin";

return (
<>
<Navbar />
<main className="container flex min-h-[calc(100vh-8rem)]">
{session?.user.role === "admin" ? props.admin : props.user}
</main>
<Footer />
</>
<div className="flex flex-row">
{session?.user.role === "admin" && <SidebarWrapper />}
<div className="flex-1">
<Navbar />
<main className="container flex min-h-[calc(100vh-8rem)]">
{isAdmin ? props.admin : props.user}
</main>
<Footer />
</div>
</div>
);
}
20 changes: 13 additions & 7 deletions apps/web/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import "@/styles/globals.css";

import { Providers } from "@/components/providers";
import { siteConfig } from "@/constant/config";
import { auth } from "@/lib/auth";
import { SessionProvider } from "next-auth/react";

export const metadata: Metadata = {
metadataBase: new URL(siteConfig.url),
Expand Down Expand Up @@ -57,7 +59,9 @@ export const viewport: Viewport = {
],
};

export default function RootLayout(props: { children: React.ReactNode }) {
export default async function RootLayout(props: { children: React.ReactNode }) {
const session = await auth();

return (
<html lang="en" suppressHydrationWarning>
<body
Expand All @@ -68,14 +72,16 @@ export default function RootLayout(props: { children: React.ReactNode }) {
)}
>
<Providers>
{props.children}
<SessionProvider session={session}>
{props.children}

<div className="fixed bottom-4 right-4">
<ThemeToggle />
</div>
<div className="fixed bottom-4 right-4">
<ThemeToggle />
</div>

<Toaster />
<Analytics />
<Toaster />
<Analytics />
</SessionProvider>
</Providers>
</body>
</html>
Expand Down
19 changes: 17 additions & 2 deletions apps/web/src/components/footer.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
"use client";

import Image from "next/image";
import { useSession } from "next-auth/react";

import { cn } from "@tanya.in/ui";
import { useTheme } from "@tanya.in/ui/theme";

import { useSidebarContext } from "./sidebar/sidebar-context";

export function Footer() {
const { resolvedTheme } = useTheme();
const { collapsed } = useSidebarContext();
const { data: session } = useSession();
const isAdmin = session?.user.role === "admin";

return (
<footer className="flex flex-wrap items-center justify-center border-t-2 p-4 sm:mx-16 sm:justify-between sm:py-0">
<p className="text-center text-xs text-default-400">
<footer
className={cn(
"flex flex-wrap items-center justify-center border-t-2 sm:mx-16 sm:px-4",
isAdmin && !collapsed ? "lg:justify-between" : "sm:justify-between",
{
"sm:justify-between": collapsed,
},
)}
>
<p className="py-2 text-center text-xs text-default-400">
Copyright © {new Date().getFullYear()} Institut Teknologi Sepuluh
Nopember
</p>
Expand Down
14 changes: 12 additions & 2 deletions apps/web/src/components/navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import { auth } from "@/lib/auth";

import { cn } from "@tanya.in/ui";

import Logo from "~/logo.svg";
import { SidebarButton } from "./sidebar/sidebar-button";
import { UserButton } from "./user";

export async function Navbar() {
const session = await auth();
const isAdmin = session?.user.role === "admin";

return (
<header className="sticky top-0 z-50 bg-content1 px-4 sm:px-8">
<header className="sticky top-0 z-10 bg-content1 px-4 sm:px-8">
<div className="flex h-16 items-center justify-between">
<Logo className="h-7 sm:h-9" />
<Logo
className={cn("hidden h-7 sm:h-9 md:block", {
invisible: isAdmin,
block: !isAdmin,
})}
/>
<SidebarButton />

<nav>
<UserButton user={session?.user} />
Expand Down
20 changes: 19 additions & 1 deletion apps/web/src/components/providers.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import * as React from "react";
import { useLockedBody } from "@/lib/hooks/useBodyLock";
import { NextUIProvider } from "@nextui-org/system";
import {
MutationCache,
Expand All @@ -13,7 +14,16 @@ import { ZodError } from "zod";

import { ThemeProvider } from "@tanya.in/ui/theme";

import { SidebarContext } from "./sidebar/sidebar-context";

export function Providers({ children }: { children: React.ReactNode }) {
const [sidebarOpen, setSidebarOpen] = React.useState(false);
const [_, setLocked] = useLockedBody(false);
const handleToggleSidebar = () => {
setSidebarOpen(!sidebarOpen);
setLocked(!sidebarOpen);
};

const [queryClient] = React.useState(
() =>
new QueryClient({
Expand Down Expand Up @@ -43,11 +53,19 @@ export function Providers({ children }: { children: React.ReactNode }) {
}),
}),
);

return (
<NextUIProvider>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<QueryClientProvider client={queryClient}>
{children}
<SidebarContext.Provider
value={{
collapsed: sidebarOpen,
setCollapsed: handleToggleSidebar,
}}
>
{children}
</SidebarContext.Provider>
</QueryClientProvider>
</ThemeProvider>
</NextUIProvider>
Expand Down
137 changes: 137 additions & 0 deletions apps/web/src/components/sidebar/collapse-items.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"use client";

import { IoChevronUp } from "react-icons/io5";

import { Accordion, AccordionItem } from "@tanya.in/ui/accordion";

interface Props {
icon: React.ReactNode;
title: string;
items: string[];
}

export const CollapseItems = ({ icon, items, title }: Props) => {
return (
<div className="flex h-full cursor-pointer items-center gap-4">
<Accordion className="px-0">
<AccordionItem
indicator={<IoChevronUp />}
classNames={{
indicator: "data-[open=true]:-rotate-180",
trigger:
"py-0 min-h-[44px] hover:bg-default-100 rounded-xl active:scale-[0.98] transition-transform px-3.5",

title:
"px-0 flex text-base gap-2 h-full items-center cursor-pointer",
}}
aria-label="Accordion 1"
title={
<div className="flex flex-row gap-2">
<span>{icon}</span>
<span>{title}</span>
</div>
}
>
<div className="pl-12">
{items.map((item, index) => (
<span
key={index}
className="flex w-full text-default-500 transition-colors hover:text-default-900"
>
{item}
</span>
))}
</div>
</AccordionItem>
</Accordion>
{/* <Accordion
title={
<div
className="flex items-center justify-between w-full py-5 px-7 rounded-8 transition-all duration-150 ease-in-out cursor-pointer hover:bg-accents2 active:scale-98"
// css={{
// gap: "$6",
// width: "100%",
// py: "$5",
// px: "$7",
// borderRadius: "8px",
// transition: "all 0.15s ease",
// "&:active": {
// transform: "scale(0.98)",
// },
// "&:hover": {
// bg: "$accents2",
// },
// }}
// justify={"between"}
onClick={handleToggle}
>
<div className="flex gap-4">
{icon}
<span
className="text-default-900 font-medium text-base"
// span
// weight={"normal"}
// size={"$base"}
// css={{
// color: "$accents9",
// }}
>
{title}
</span>
</div>
<ChevronUpIcon
className={clsx(
open ? "rotate-180" : "rotate-0",
"transition-all duration-300 ease-in-out transform"
)}
// css={{
// transition: "transform 0.3s ease",
// transform: open ? "rotate(-180deg)" : "rotate(0deg)",
// }}
/>
</div>
}
// css={{
// width: "100%",
// "& .nextui-collapse-view": {
// p: "0",
// },
// "& .nextui-collapse-content": {
// marginTop: "$1",
// padding: "0px",
// },
// }}
divider={false}
showArrow={false}
>
{items.map((item, index) => (
<div
className="flex flex-col pl-8"
key={index}
// direction={"column"}
// css={{
// paddingLeft: "$16",
// }}
>
<span
className="text-default-400 font-normal text-md"
// span
// weight={"normal"}
// size={"$md"}
// css={{
// color: "$accents8",
// cursor: "pointer",
// "&:hover": {
// color: "$accents9",
// },
// }}
>
{item}
</span>
</div>
))}
</Accordion> */}
</div>
);
};
27 changes: 27 additions & 0 deletions apps/web/src/components/sidebar/sidebar-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use client";

import { useSession } from "next-auth/react";
import { IoMenu } from "react-icons/io5";

import { Button } from "@tanya.in/ui/button";

import { useSidebarContext } from "./sidebar-context";

export const SidebarButton = () => {
const { setCollapsed } = useSidebarContext();
const { data: session } = useSession();

return (
session?.user.role === "admin" && (
<Button
isIconOnly
variant="light"
aria-label="Menu"
onClick={setCollapsed}
className="md:hidden"
>
<IoMenu size={28} />
</Button>
)
);
};
14 changes: 14 additions & 0 deletions apps/web/src/components/sidebar/sidebar-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createContext, useContext } from "react";

interface SidebarContext {
collapsed: boolean;
setCollapsed?: () => void;
}

export const SidebarContext = createContext<SidebarContext>({
collapsed: false,
});

export const useSidebarContext = () => {
return useContext(SidebarContext);
};
Loading

0 comments on commit e290b90

Please sign in to comment.