Skip to content

Commit

Permalink
Merge pull request #16 from TEDx-SJEC/admin
Browse files Browse the repository at this point in the history
Admin
  • Loading branch information
joywin2003 authored Sep 29, 2024
2 parents a80977d + 19cb6df commit 5cb37c6
Show file tree
Hide file tree
Showing 8 changed files with 412 additions and 61 deletions.
34 changes: 34 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"bullmq": "^5.13.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"framer-motion": "^11.9.0",
"ioredis": "^5.4.1",
"jest": "^29.7.0",
"lodash.debounce": "^4.0.8",
Expand All @@ -48,6 +49,7 @@
"react": "^18.3.1",
"react-dom": "^18",
"react-email": "^3.0.1",
"react-icons": "^5.3.0",
"react-hook-form": "^7.53.0",
"resend": "^4.0.0",
"sonner": "^1.5.0",
Expand Down
13 changes: 13 additions & 0 deletions src/app/actions/get-user-by-id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import prisma from "@/server/db"; // Adjust the import based on your structure

export async function getUserById(userId: string) {
const user = await prisma.user.findUnique({
where: { id: userId },
select: {
id: true,
role: true, // Include any other fields you need
},
});

return user;
}
88 changes: 88 additions & 0 deletions src/app/admin/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
}

@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
}

@layer utilities {
.text-balance {
text-wrap: balance;
}
}

@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--radius: 0.5rem;
}
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
}

@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
27 changes: 27 additions & 0 deletions src/app/admin/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use client";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import Providers from "@/components/Layout/Provider";
import { AdminNavbar } from "@/components/Admin/Navbar/navbar";

const inter = Inter({ subsets: ["latin"] });

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
<Providers>
<div className="flex">
<AdminNavbar />
{children}
</div>
</Providers>
</body>
</html>
);
}
189 changes: 189 additions & 0 deletions src/components/Admin/Navbar/navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
"use client";
import React, { Dispatch, SetStateAction, useState } from "react";
import { IconType } from "react-icons";
import { FiChevronDown, FiChevronsRight, FiUser } from "react-icons/fi";
import { RiCoupon3Line } from "react-icons/ri";
import { motion } from "framer-motion";
import Link from "next/link";

export const AdminNavbar = () => {
return (
<div className="flex bg-indigo-50 h-screen">
<Sidebar />
<NavbarContent />
</div>
);
};

const Sidebar = () => {
const [open, setOpen] = useState(true);
const [selected, setSelected] = useState("Dashboard");

return (
<motion.nav
layout
className="sticky top-0 bottom-0 h-screen shrink-0 border-r border-slate-300 bg-white p-2"
style={{
width: open ? "225px" : "fit-content",
}}
>
<TitleSection open={open} />

<div className="space-y-1">
<Option
Icon={RiCoupon3Line}
title="Coupon"
selected={selected}
setSelected={setSelected}
open={open}
href="/admin"
/>
<Option
Icon={FiUser}
title="Users"
selected={selected}
setSelected={setSelected}
open={open}
href="/admin/users"
/>
</div>

<ToggleClose open={open} setOpen={setOpen} />
</motion.nav>
);
};

const Option = ({
Icon,
title,
selected,
setSelected,
open,
notifs,
href,
}: {
Icon: IconType;
title: string;
selected: string;
setSelected: Dispatch<SetStateAction<string>>;
open: boolean;
notifs?: number;
href?: string;
}) => {
return (
<Link href={href ?? ""}>
<motion.button
layout
onClick={() => setSelected(title)}
className={`relative flex h-10 w-full items-center rounded-md transition-colors ${
selected === title ? "bg-indigo-100 text-indigo-800" : "text-slate-500 hover:bg-slate-100"
}`}
>
<motion.div layout className="grid h-full w-10 place-content-center text-lg">
<Icon />
</motion.div>
{open && (
<motion.span
layout
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.125 }}
className="text-xs font-medium"
>
{title}
</motion.span>
)}

{notifs && open && (
<motion.span
initial={{ scale: 0, opacity: 0 }}
animate={{
opacity: 1,
scale: 1,
}}
style={{ y: "-50%" }}
transition={{ delay: 0.5 }}
className="absolute right-2 top-1/2 size-4 rounded bg-indigo-500 text-xs text-white"
>
{notifs}
</motion.span>
)}
</motion.button>
</Link>
);
};

const TitleSection = ({ open }: { open: boolean }) => {
return (
<div className="mb-3 border-b border-slate-300 pb-3">
<div className="flex cursor-pointer items-center justify-between rounded-md transition-colors hover:bg-slate-100">
<div className="flex items-center gap-2">
<Logo />
{open && (
<motion.div
layout
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.125 }}
>
<span className="block text-xs font-semibold">Tedxsjec</span>
<span className="block text-xs text-slate-500">Admin Page</span>
</motion.div>
)}
</div>
{open && <FiChevronDown className="mr-2" />}
</div>
</div>
);
};

const Logo = () => {
// Temp logo from https://logoipsum.com/
return (
<motion.div layout className="grid size-10 shrink-0 place-content-center rounded-md bg-indigo-600">
<svg
width="24"
height="auto"
viewBox="0 0 50 39"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="fill-slate-50"
>
<path d="M16.4992 2H37.5808L22.0816 24.9729H1L16.4992 2Z" stopColor="#000000"></path>
<path
d="M17.4224 27.102L11.4192 36H33.5008L49 13.0271H32.7024L23.2064 27.102H17.4224Z"
stopColor="#000000"
></path>
</svg>
</motion.div>
);
};

const ToggleClose = ({ open, setOpen }: { open: boolean; setOpen: Dispatch<SetStateAction<boolean>> }) => {
return (
<motion.button
layout
onClick={() => setOpen((pv) => !pv)}
className="absolute bottom-0 left-0 right-0 border-t border-slate-300 transition-colors hover:bg-slate-100"
>
<div className="flex items-center p-2">
<motion.div layout className="grid size-10 place-content-center text-lg">
<FiChevronsRight className={`transition-transform ${open && "rotate-180"}`} />
</motion.div>
{open && (
<motion.span
layout
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.125 }}
className="text-xs font-medium"
>
Hide
</motion.span>
)}
</div>
</motion.button>
);
};

const NavbarContent = () => <div className="h-[200vh] w-full"></div>;
Loading

0 comments on commit 5cb37c6

Please sign in to comment.