diff --git a/public/admin-dashboard/icons/arrowUp.svg b/public/admin-dashboard/icons/arrowUp.svg new file mode 100644 index 000000000..8d1856ede --- /dev/null +++ b/public/admin-dashboard/icons/arrowUp.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/admin-dashboard/icons/box.svg b/public/admin-dashboard/icons/box.svg new file mode 100644 index 000000000..a07b9e2de --- /dev/null +++ b/public/admin-dashboard/icons/box.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/admin-dashboard/icons/dollarSign.svg b/public/admin-dashboard/icons/dollarSign.svg new file mode 100644 index 000000000..a6c75e629 --- /dev/null +++ b/public/admin-dashboard/icons/dollarSign.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/admin-dashboard/icons/user.svg b/public/admin-dashboard/icons/user.svg new file mode 100644 index 000000000..5a3075414 --- /dev/null +++ b/public/admin-dashboard/icons/user.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/app/dashboard/(admin)/_components/layout/Sidebar/index.tsx b/src/app/dashboard/(admin)/_components/layout/Sidebar/index.tsx index 8a5c04adf..715c4ce55 100644 --- a/src/app/dashboard/(admin)/_components/layout/Sidebar/index.tsx +++ b/src/app/dashboard/(admin)/_components/layout/Sidebar/index.tsx @@ -1,6 +1,14 @@ "use client"; -import { Box, House, LucideProps, Mail, Settings, Users } from "lucide-react"; +import { + Box, + House, + List, + LucideProps, + Mail, + Settings, + Users, +} from "lucide-react"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { FC, ForwardRefExoticComponent, RefAttributes } from "react"; @@ -10,43 +18,43 @@ import DashboardLogo from "../logo"; const sideItems = [ { route: "Dashboard", - link: "/admin/dashboard", + link: "/dashboard/admin/dashboard", icon: House, id: "dashboard", }, { route: "Products", - link: "/admin/products", + link: "/dashboard/admin/products", icon: Box, id: "products", }, { route: "Users", - link: "/admin/users", + link: "/dashboard/admin/users", icon: Users, id: "users", }, { route: "Email Templates", - link: "/admin/email", + link: "/dashboard/admin/email", icon: Mail, id: "email", }, { route: "Squeeze Pages", - link: "/admin/squeeze-pages", + link: "/dashboard/admin/squeeze-pages", icon: Users, id: "squeeze", }, { route: "Waitlist Page", - link: "/admin/waitlist-page", - icon: Mail, + link: "/dashboard/admin/waitlist-page", + icon: List, id: "waitlist", }, { route: "Settings", - link: "/admin/settings", + link: "/dashboard/admin/settings", icon: Settings, id: "settings", }, @@ -67,18 +75,19 @@ const Sidebar: FC = ({ currenPathName, }) => { const pathname = usePathname(); - const currentPath = pathname?.split("/")[2]; + const currentPath = pathname?.split("/")[3]; + return ( - + - + {sideNavitems.map((item, index) => ( {item.route} diff --git a/src/app/dashboard/(admin)/_components/layout/Sidebar/sidebar.test.tsx b/src/app/dashboard/(admin)/_components/layout/Sidebar/sidebar.test.tsx index 4c35021e9..9a0355ec8 100644 --- a/src/app/dashboard/(admin)/_components/layout/Sidebar/sidebar.test.tsx +++ b/src/app/dashboard/(admin)/_components/layout/Sidebar/sidebar.test.tsx @@ -1,48 +1,48 @@ -import Sidebar from "."; -import { Box, House, Mail, Settings, Users } from "lucide-react"; +import { Box, House, List, Mail, Settings, Users } from "lucide-react"; import { render, screen } from "~/test/utils"; +import Sidebar from "./index"; const sideItems = [ { route: "Dashboard", - link: "/admin/dashboard", + link: "/dashboard/admin/dashboard", icon: House, id: "dashboard", }, { route: "Products", - link: "/admin/products", + link: "/dashboard/admin/products", icon: Box, id: "products", }, { route: "Users", - link: "/admin/users", + link: "/dashboard/admin/users", icon: Users, id: "users", }, { route: "Email Templates", - link: "/admin/email", + link: "/dashboard/admin/email", icon: Mail, id: "email", }, { route: "Squeeze Pages", - link: "/admin/squeeze-pages", + link: "/dashboard/admin/squeeze-pages", icon: Users, id: "squeeze", }, { route: "Waitlist Page", - link: "/admin/waitlist-page", - icon: Mail, + link: "/dashboard/admin/waitlist-page", + icon: List, id: "waitlist", }, { route: "Settings", - link: "/admin/settings", + link: "/dashboard/admin/settings", icon: Settings, id: "settings", }, diff --git a/src/app/dashboard/(admin)/_components/layout/navbar/index.tsx b/src/app/dashboard/(admin)/_components/layout/navbar/index.tsx index 5b8f3d713..8c2ba1c8e 100644 --- a/src/app/dashboard/(admin)/_components/layout/navbar/index.tsx +++ b/src/app/dashboard/(admin)/_components/layout/navbar/index.tsx @@ -10,8 +10,11 @@ import UnreadNotificationCard from "../../unread-notification-card/UnreadNotific const DashboardNavbar = () => { return ( - - + + { data-testid="input" /> - + diff --git a/src/app/dashboard/(admin)/admin/dashboard/client.tsx b/src/app/dashboard/(admin)/admin/dashboard/client.tsx new file mode 100644 index 000000000..676bccc57 --- /dev/null +++ b/src/app/dashboard/(admin)/admin/dashboard/client.tsx @@ -0,0 +1,48 @@ +"use client"; + +import CardComponent from "~/components/adminDashboard/CardComponent"; +import { cardData } from "~/components/adminDashboard/cardData"; +import { Chart } from "~/components/adminDashboard/Chart"; +import { chartConfig, chartData } from "~/components/adminDashboard/chartData"; +import { data, gradients } from "~/components/adminDashboard/productData"; +import TopProductsComponent from "~/components/adminDashboard/TopProductsComponent"; +import { Card } from "~/components/ui/card"; + +const Client = () => { + return ( + + + + Overview + + Showing records from the last ..... + + + + + + {cardData.map((card, index) => ( + + ))} + + + + + + Overview + + + + + + + ); +}; + +export default Client; diff --git a/src/app/dashboard/(admin)/admin/dashboard/page.tsx b/src/app/dashboard/(admin)/admin/dashboard/page.tsx index 2779a4430..1f476ebc5 100644 --- a/src/app/dashboard/(admin)/admin/dashboard/page.tsx +++ b/src/app/dashboard/(admin)/admin/dashboard/page.tsx @@ -1,5 +1,13 @@ -const page = () => { - return Admin dashboard; +import Client from "./client"; + +export const metadata = { + title: "Dashboard", + description: + "Super Admin Dashboard using ShadCN UI components, adhering to design specifications and best practices for accessibility.", +}; + +const AdminDashboardPage = () => { + return ; }; -export default page; +export default AdminDashboardPage; diff --git a/src/app/dashboard/(admin)/layout.tsx b/src/app/dashboard/(admin)/layout.tsx index 05f695776..f348cb15d 100644 --- a/src/app/dashboard/(admin)/layout.tsx +++ b/src/app/dashboard/(admin)/layout.tsx @@ -9,11 +9,11 @@ export default function AdminLayout({ children: React.ReactNode; }) { return ( - + - - - + + + {children} diff --git a/src/components/adminDashboard/CardComponent.tsx b/src/components/adminDashboard/CardComponent.tsx new file mode 100644 index 000000000..9e2fca3ed --- /dev/null +++ b/src/components/adminDashboard/CardComponent.tsx @@ -0,0 +1,47 @@ +import Image from "next/image"; +import { FC } from "react"; + +interface CardProperties { + title: string; + value: string; + description: string; + icon: string; +} + +const CardComponent: FC = ({ + title, + value, + description, + icon, +}) => { + return ( + <> + + + + {title} + + + + + + + + {value} + + + {description} + + + + > + ); +}; + +export default CardComponent; diff --git a/src/components/adminDashboard/Chart.tsx b/src/components/adminDashboard/Chart.tsx new file mode 100644 index 000000000..52f0a9f6a --- /dev/null +++ b/src/components/adminDashboard/Chart.tsx @@ -0,0 +1,58 @@ +import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts"; + +import { CardContent } from "~/components/ui/card"; +import { + ChartConfig, + ChartContainer, + ChartTooltip, + ChartTooltipContent, +} from "~/components/ui/chart"; + +type ChartProperties = { + chartData: { month: string; revenue: number }[]; + chartConfig: ChartConfig; +}; + +export function Chart({ chartData = [], chartConfig }: ChartProperties) { + return ( + <> + + + + + + value.slice(0, 3)} + /> + `$${value}`} + /> + } + /> + + + + + + > + ); +} diff --git a/src/components/adminDashboard/TopProductsComponent.tsx b/src/components/adminDashboard/TopProductsComponent.tsx new file mode 100644 index 000000000..d25f26505 --- /dev/null +++ b/src/components/adminDashboard/TopProductsComponent.tsx @@ -0,0 +1,83 @@ +import Image from "next/image"; +import React from "react"; + +import { Card } from "../ui/card"; + +type ProductData = { + name: string; + amount: string; +}; + +type TopProductsProperties = { + data: ProductData[]; + gradients: string[]; +}; + +const ExternalLinkIcon = () => { + return ( + <> + + > + ); +}; + +const TopProductsComponent: React.FC = ({ + data, + gradients, +}) => { + return ( + + + + + Top Products + + + Your top selling products appear here. + + + + View All + + + + + {data.map((item, index) => ( + + + + + + {item.name} + + + + + {item.amount} + + + ))} + + + ); +}; + +export default TopProductsComponent; diff --git a/src/components/adminDashboard/cardData.ts b/src/components/adminDashboard/cardData.ts new file mode 100644 index 000000000..96692068e --- /dev/null +++ b/src/components/adminDashboard/cardData.ts @@ -0,0 +1,33 @@ +type CardData = { + title: string; + value: string; + description: string; + icon: string; +}; + +export const cardData: CardData[] = [ + { + title: "Total Revenue", + value: "$45,000.00", + description: "+20% from last month", + icon: `/admin-dashboard/icons/dollarSign.svg`, + }, + { + title: "Total Users", + value: "+4,000", + description: "+10% from last month", + icon: `/admin-dashboard/icons/user.svg`, + }, + { + title: "Total Products", + value: "1,000", + description: "+20% from last month", + icon: `/admin-dashboard/icons/box.svg`, + }, + { + title: "Lifetime Sales", + value: "$450,000.00", + description: "+150% from last month", + icon: `/admin-dashboard/icons/arrowUp.svg`, + }, +]; diff --git a/src/components/adminDashboard/chartData.ts b/src/components/adminDashboard/chartData.ts new file mode 100644 index 000000000..4314df6e2 --- /dev/null +++ b/src/components/adminDashboard/chartData.ts @@ -0,0 +1,30 @@ +import { ChartConfig } from "~/components/ui/chart"; + +export const generateRandomRevenue = () => + (Math.floor(Math.random() * 11) + 1) * 500; + +export const chartData = [ + { month: "January", revenue: generateRandomRevenue() }, + { month: "February", revenue: generateRandomRevenue() }, + { month: "March", revenue: generateRandomRevenue() }, + { month: "April", revenue: generateRandomRevenue() }, + { month: "May", revenue: generateRandomRevenue() }, + { month: "June", revenue: generateRandomRevenue() }, + { month: "July", revenue: generateRandomRevenue() }, + { month: "August", revenue: generateRandomRevenue() }, + { month: "September", revenue: generateRandomRevenue() }, + { month: "October", revenue: generateRandomRevenue() }, + { month: "November", revenue: generateRandomRevenue() }, + { month: "December", revenue: generateRandomRevenue() }, +]; + +export const chartConfig: ChartConfig = { + desktop: { + label: "Revenue", + color: "#F97316", + }, + mobile: { + label: "Mobile", + color: "#F97316", + }, +}; diff --git a/src/components/adminDashboard/productData.ts b/src/components/adminDashboard/productData.ts new file mode 100644 index 000000000..697e26b0f --- /dev/null +++ b/src/components/adminDashboard/productData.ts @@ -0,0 +1,37 @@ +const data = [ + { + name: "The Lemonade blender", + amount: "500 sales", + }, + { + name: "Bean Cake Powder", + amount: "250 sales", + }, + { + name: "Flour Mixer", + amount: "230 sales", + }, + { + name: "Blender", + amount: "500 sales", + }, + { + name: "A Food Product", + amount: "150 sales", + }, + { + name: "Cake Powder", + amount: "100 sales", + }, +]; + +const gradients = [ + " linear-gradient(180deg, #F6C790 0%, #E77F1E 100%)", + "linear-gradient(180deg, #D6F690 0%, #D7E71E 100%)", + "linear-gradient(180deg, #9290F6 0%, #461EE7 100%)", + " linear-gradient(180deg, #F690A8 0%, #E71E4E 100%)", + "linear-gradient(180deg, #B4F690 0%, #64E71E 100%)", + "linear-gradient(180deg, #E990F6 0%, #CB1EE7 100%)", +]; + +export { data, gradients }; diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx index 6b39931f0..153881b0a 100644 --- a/src/components/ui/card.tsx +++ b/src/components/ui/card.tsx @@ -36,8 +36,8 @@ const CardTitle = React.forwardRef< diff --git a/src/components/ui/chart.tsx b/src/components/ui/chart.tsx new file mode 100644 index 000000000..94664dbb6 --- /dev/null +++ b/src/components/ui/chart.tsx @@ -0,0 +1,367 @@ +"use client"; + +import * as React from "react"; +import * as RechartsPrimitive from "recharts"; + +import { cn } from "~/lib/utils"; + +// Format: { THEME_NAME: CSS_SELECTOR } +const THEMES = { light: "", dark: ".dark" } as const; + +export type ChartConfig = { + [k in string]: { + label?: React.ReactNode; + icon?: React.ComponentType; + } & ( + | { color?: string; theme?: never } + | { color?: never; theme: Record } + ); +}; + +type ChartContextProperties = { + config: ChartConfig; +}; + +const ChartContext = React.createContext( + undefined, +); + +function useChart() { + const context = React.useContext(ChartContext); + + if (!context) { + throw new Error("useChart must be used within a "); + } + + return context; +} + +const ChartContainer = React.forwardRef< + HTMLDivElement, + React.ComponentProps<"div"> & { + config: ChartConfig; + children: React.ComponentProps< + typeof RechartsPrimitive.ResponsiveContainer + >["children"]; + } +>(({ id, className, children, config, ...properties }, reference) => { + const uniqueId = React.useId(); + const chartId = `chart-${id || uniqueId.replaceAll(":", "")}`; + + return ( + + + + + {children} + + + + ); +}); +ChartContainer.displayName = "Chart"; + +const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { + const colorConfig = Object.entries(config).filter( + ([, config]) => config.theme || config.color, + ); + + if (colorConfig.length === 0) { + return; + } + + return ( +
+ Showing records from the last ..... +
+ Your top selling products appear here. +
+ {item.name} +
+ {item.amount} +