diff --git a/next.config.mjs b/next.config.mjs index 0503668d8..d948ed61e 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -11,6 +11,7 @@ const nextConfig = { }, ], }, + transpilePackages: ["lucide-react"], }; export default nextConfig; \ No newline at end of file diff --git a/package.json b/package.json index 13cd018d8..d1788c73d 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cookies-next": "^4.2.1", + "date-fns": "^3.6.0", "framer-motion": "^11.3.8", "input-otp": "^1.2.4", "jest-axe": "^9.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8b10f461a..78477e3b2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -83,9 +83,12 @@ importers: cookies-next: specifier: ^4.2.1 version: 4.2.1 + date-fns: + specifier: ^3.6.0 + version: 3.6.0 framer-motion: specifier: ^11.3.8 - version: 11.3.17(@emotion/is-prop-valid@0.8.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 11.3.19(@emotion/is-prop-valid@0.8.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) input-otp: specifier: ^1.2.4 version: 1.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -3232,8 +3235,8 @@ packages: react-dom: optional: true - framer-motion@11.3.17: - resolution: {integrity: sha512-LZcckvZL8Rjod03bud8LQcp+R0PLmWIlOSu+NVc+v6Uh43fQr4IBsEAX7sSn7CdBQ1L0fZ/IqSXZVPnGFSMxHw==} + framer-motion@11.3.19: + resolution: {integrity: sha512-+luuQdx4AsamyMcvzW7jUAJYIKvQs1KE7oHvKkW3eNzmo0S+3PSDWjBuQkuIP9WyneGnKGMLUSuHs8OP7jKpQg==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 @@ -8695,7 +8698,7 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - framer-motion@11.3.17(@emotion/is-prop-valid@0.8.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + framer-motion@11.3.19(@emotion/is-prop-valid@0.8.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: tslib: 2.6.3 optionalDependencies: diff --git a/public/admin-dashboard/icons/arrowUp.svg b/public/admin-dashboard/icons/arrowUp.svg deleted file mode 100644 index 8d1856ede..000000000 --- a/public/admin-dashboard/icons/arrowUp.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/public/admin-dashboard/icons/box.svg b/public/admin-dashboard/icons/box.svg deleted file mode 100644 index a07b9e2de..000000000 --- a/public/admin-dashboard/icons/box.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/public/admin-dashboard/icons/dollarSign.svg b/public/admin-dashboard/icons/dollarSign.svg deleted file mode 100644 index a6c75e629..000000000 --- a/public/admin-dashboard/icons/dollarSign.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/public/admin-dashboard/icons/user.svg b/public/admin-dashboard/icons/user.svg deleted file mode 100644 index 5a3075414..000000000 --- a/public/admin-dashboard/icons/user.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/app/dashboard/(admin)/admin/client.tsx b/src/app/dashboard/(admin)/admin/client.tsx index 80d39e896..97f61eabb 100644 --- a/src/app/dashboard/(admin)/admin/client.tsx +++ b/src/app/dashboard/(admin)/admin/client.tsx @@ -1,11 +1,11 @@ "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 CardComponent from "~/components/common/DashboardCard/CardComponent"; import { Card } from "~/components/ui/card"; const Client = () => { diff --git a/src/app/dashboard/(admin)/admin/products/page.tsx b/src/app/dashboard/(admin)/admin/products/page.tsx index 72b561622..dff3f3025 100644 --- a/src/app/dashboard/(admin)/admin/products/page.tsx +++ b/src/app/dashboard/(admin)/admin/products/page.tsx @@ -2,9 +2,9 @@ import { CirclePlus, Filter } from "lucide-react"; -import CardComponent from "~/components/adminDashboard/CardComponent"; import { cardData } from "~/components/adminDashboard/cardData"; import { Button } from "~/components/common/common-button"; +import CardComponent from "~/components/common/DashboardCard/CardComponent"; import { DropdownMenu, DropdownMenuContent, diff --git a/src/app/dashboard/(admin)/admin/users/data/user-dummy-data.ts b/src/app/dashboard/(admin)/admin/users/data/user-dummy-data.ts index 07d39ba03..9a0c933ec 100644 --- a/src/app/dashboard/(admin)/admin/users/data/user-dummy-data.ts +++ b/src/app/dashboard/(admin)/admin/users/data/user-dummy-data.ts @@ -1,8 +1,10 @@ +import type { LucideIconName } from "~/components/common/lucide-icon"; + interface UserCardData { title: string; value: number; description: string; - icon: string; + icon: LucideIconName; } interface UserStatusProperties { @@ -23,19 +25,19 @@ export const userCardData: UserCardData[] = [ title: "Total Users", value: 4000, description: "+10% from last month", - icon: `/admin-dashboard/icons/user.svg`, + icon: "user", }, { title: "Active Users", value: 1500, description: "+20% from last month", - icon: `/admin-dashboard/icons/box.svg`, + icon: "box", }, { title: "Deleted Users", value: 2500, description: "+150% from last month", - icon: `/admin-dashboard/icons/arrowUp.svg`, + icon: "arrow-up", }, ]; diff --git a/src/app/dashboard/(admin)/admin/users/page.tsx b/src/app/dashboard/(admin)/admin/users/page.tsx index 7f58236cc..63711d856 100644 --- a/src/app/dashboard/(admin)/admin/users/page.tsx +++ b/src/app/dashboard/(admin)/admin/users/page.tsx @@ -9,8 +9,8 @@ import { } from "lucide-react"; import { useState } from "react"; -import CardComponent from "~/components/adminDashboard/CardComponent"; import CustomButton from "~/components/common/common-button/common-button"; +import CardComponent from "~/components/common/DashboardCard/CardComponent"; import { Button } from "~/components/ui/button"; import { DropdownMenu, diff --git a/src/app/dashboard/(user-dashboard)/(user-metrics)/analytics/page.tsx b/src/app/dashboard/(user-dashboard)/(user-metrics)/analytics/page.tsx new file mode 100644 index 000000000..b1477f101 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/(user-metrics)/analytics/page.tsx @@ -0,0 +1,3 @@ +export default function DashboardAnalytics() { + return
ANALYTICS
; +} diff --git a/src/app/dashboard/(user-dashboard)/(user-metrics)/layout.tsx b/src/app/dashboard/(user-dashboard)/(user-metrics)/layout.tsx new file mode 100644 index 000000000..015021e12 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/(user-metrics)/layout.tsx @@ -0,0 +1,40 @@ +import type { PropsWithChildren } from "react"; + +import { DateSelector } from "~/app/dashboard/(user-dashboard)/_components/layout/dateselector"; +import { Tablinks } from "~/app/dashboard/(user-dashboard)/_components/layout/tablinks"; +import { Button } from "~/components/common/common-button"; + +const links = [ + { + title: "Overview", + href: "/dashboard", + }, + { + title: "Analytics", + href: "/dashboard/analytics", + }, + { + title: "Reports", + href: "/dashboard/reports", + }, +]; + +export default function UserMetricsLayout({ children }: PropsWithChildren) { + return ( +
+
+
+

Dashboard

+ +
+
+ + +
+
+
{children}
+
+ ); +} diff --git a/src/app/dashboard/(user-dashboard)/(user-metrics)/page.tsx b/src/app/dashboard/(user-dashboard)/(user-metrics)/page.tsx new file mode 100644 index 000000000..6df461c23 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/(user-metrics)/page.tsx @@ -0,0 +1,19 @@ +import { OverviewChart } from "~/app/dashboard/(user-dashboard)/_components/overview-chart"; +import { OverviewRevenue } from "~/app/dashboard/(user-dashboard)/_components/overview-revenue"; +import { OverviewSales } from "~/app/dashboard/(user-dashboard)/_components/overview-sales"; + +export default function DashboardOverview() { + return ( +
+
+ +
+
+ +
+ +
+
+
+ ); +} diff --git a/src/app/dashboard/(user-dashboard)/(user-metrics)/reports/page.tsx b/src/app/dashboard/(user-dashboard)/(user-metrics)/reports/page.tsx new file mode 100644 index 000000000..b6815d6f8 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/(user-metrics)/reports/page.tsx @@ -0,0 +1,3 @@ +export default function DashboardReports() { + return
REPORTS
; +} diff --git a/src/app/dashboard/(user-dashboard)/_components/RecentSales.tsx b/src/app/dashboard/(user-dashboard)/_components/RecentSales.tsx new file mode 100644 index 000000000..59191a410 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/_components/RecentSales.tsx @@ -0,0 +1,87 @@ +import React from "react"; + +import { Card } from "~/components/ui/card"; + +type ProductData = { + name: string; + amount: string; + email: string; +}; + +type TopProductsProperties = { + data: ProductData[]; + gradients: string[]; + noOfSales: number; +}; + +const RecentSales: React.FC = ({ + data, + gradients, + noOfSales, +}) => { + return ( + +
+
+

+ Recent Sales +

+

+ You made {noOfSales} sales this month +

+
+
+ +
+ ); +}; + +export default RecentSales; diff --git a/src/app/dashboard/(user-dashboard)/_components/layout/dateselector/index.tsx b/src/app/dashboard/(user-dashboard)/_components/layout/dateselector/index.tsx new file mode 100644 index 000000000..5152363ac --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/_components/layout/dateselector/index.tsx @@ -0,0 +1,74 @@ +"use client"; + +import { format, subMonths } from "date-fns"; +import { CalendarDaysIcon } from "lucide-react"; +import { useState } from "react"; +import { DateRange } from "react-day-picker"; + +import { Button } from "~/components/common/common-button"; +import { Calendar } from "~/components/ui/calendar"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "~/components/ui/popover"; +import { cn } from "~/lib/utils"; + +type DateSelectorProperties = { + from?: Date; + to?: Date; + className?: string; +}; +export function DateSelector({ + from = subMonths(new Date(), 1), + to = new Date(), + className, +}: DateSelectorProperties) { + const [date, setDate] = useState({ + from, + to, + }); + + return ( +
+ + + + + + + + +
+ ); +} diff --git a/src/app/dashboard/(user-dashboard)/_components/layout/tablinks/index.tsx b/src/app/dashboard/(user-dashboard)/_components/layout/tablinks/index.tsx new file mode 100644 index 000000000..6caf7bfd5 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/_components/layout/tablinks/index.tsx @@ -0,0 +1,34 @@ +"use client"; + +import type { Route } from "next"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; + +import { cn } from "~/lib/utils"; + +type TablinksProperties = { + links: { + title: string; + href: Route; + }[]; +}; + +export function Tablinks({ links }: TablinksProperties) { + const pathname = usePathname(); + return ( + + ); +} diff --git a/src/app/dashboard/(user-dashboard)/_components/overview-chart/index.tsx b/src/app/dashboard/(user-dashboard)/_components/overview-chart/index.tsx new file mode 100644 index 000000000..fa370d191 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/_components/overview-chart/index.tsx @@ -0,0 +1,18 @@ +"use client"; + +import { Chart } from "~/components/userDashboard/Chart"; +import { chartConfig, chartData } from "~/components/userDashboard/chartData"; + +type OverviewChartProperties = { + className?: string; +}; +export function OverviewChart({ className }: OverviewChartProperties) { + return ( + + ); +} diff --git a/src/app/dashboard/(user-dashboard)/_components/overview-revenue/index.tsx b/src/app/dashboard/(user-dashboard)/_components/overview-revenue/index.tsx new file mode 100644 index 000000000..226902537 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/_components/overview-revenue/index.tsx @@ -0,0 +1,18 @@ +import { revenueData } from "~/app/dashboard/(user-dashboard)/_components/overview-revenue/revenueData"; +import CardComponent from "~/components/common/DashboardCard/CardComponent"; + +export function OverviewRevenue() { + return ( +
+ {revenueData.map((card, index) => ( + + ))} +
+ ); +} diff --git a/src/app/dashboard/(user-dashboard)/_components/overview-revenue/revenueData.ts b/src/app/dashboard/(user-dashboard)/_components/overview-revenue/revenueData.ts new file mode 100644 index 000000000..584661a32 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/_components/overview-revenue/revenueData.ts @@ -0,0 +1,35 @@ +import type { LucideIconName } from "~/components/common/lucide-icon"; + +type CardData = { + title: string; + value: string; + description: string; + icon: LucideIconName; +}; + +export const revenueData: CardData[] = [ + { + title: "Total Revenue", + value: "$45,000.00", + description: "+20% from last month", + icon: "dollar-sign", + }, + { + title: "Subscriptions", + value: "+2,350", + description: "+150% from last month", + icon: "users", + }, + { + title: "Sales", + value: "15,000", + description: "+10% from last month", + icon: "credit-card", + }, + { + title: "Active Now", + value: "574", + description: "+201 since last hour", + icon: "trending-up", + }, +]; diff --git a/src/app/dashboard/(user-dashboard)/_components/overview-sales/index.tsx b/src/app/dashboard/(user-dashboard)/_components/overview-sales/index.tsx new file mode 100644 index 000000000..95952ac66 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/_components/overview-sales/index.tsx @@ -0,0 +1,11 @@ +import RecentSales from "~/app/dashboard/(user-dashboard)/_components/RecentSales"; +import { + data, + gradients, +} from "~/app/dashboard/(user-dashboard)/_components/salesData"; + +export function OverviewSales() { + return ( + + ); +} diff --git a/src/app/dashboard/(user-dashboard)/_components/salesData.ts b/src/app/dashboard/(user-dashboard)/_components/salesData.ts new file mode 100644 index 000000000..c8143118a --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/_components/salesData.ts @@ -0,0 +1,43 @@ +const data = [ + { + name: "Jackson Lee", + amount: "2999", + email: "jackson.lee@gmail.com", + }, + { + name: "Olivia Martin", + amount: "7999", + email: "olivia.martin@gmail.com", + }, + { + name: "Joseph Chernysuck", + amount: "5999", + email: "olivia.martin@gmail.com", + }, + { + name: "Paul Halland", + amount: "12999", + email: "olivia.martin@gmail.com", + }, + { + name: "Eden Hazard", + amount: "3999", + email: "jackson.lee@gmail.com", + }, + { + name: "Ronaldo Messi", + amount: "4999", + email: "jackson.lee@gmail.com", + }, +]; + +const gradients = [ + "linear-gradient(180deg, #F6C790 0%, #E77F1E 100%)", + "linear-gradient(180deg, #F81404 0%, #0F172A 100%)", + "linear-gradient(180deg, rgba(4, 190, 248, 0.20) 0%, #0AB025 100%)", + "linear-gradient(180deg, #FFF 0%, #7F838D 20.86%, #0F172A 100%)", + "linear-gradient(180deg, #1E1D1C 0%, #3A1EE7 100%)", + "linear-gradient(180deg, #EF9B38 0%, #7EA7D9 100%)", +]; + +export { data, gradients }; diff --git a/src/app/dashboard/(user-dashboard)/layout.tsx b/src/app/dashboard/(user-dashboard)/layout.tsx index 472a3d467..9518eb265 100644 --- a/src/app/dashboard/(user-dashboard)/layout.tsx +++ b/src/app/dashboard/(user-dashboard)/layout.tsx @@ -8,7 +8,7 @@ export default function AdminLayout({ children: React.ReactNode; }) { return ( -
+
{children} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 0baa2ab7a..94cfbdaa2 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -21,7 +21,7 @@ export default function RootLayout({ return ( -
+
{children} diff --git a/src/components/adminDashboard/CardComponent.tsx b/src/components/adminDashboard/CardComponent.tsx deleted file mode 100644 index df4594d85..000000000 --- a/src/components/adminDashboard/CardComponent.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import Image from "next/image"; -import { FC } from "react"; - -interface CardProperties { - title: string; - value: string | number; - description: string; - icon: string; -} - -const CardComponent: FC = ({ - title, - value, - description, - icon, -}) => { - return ( - <> -
-
-

- {title} -

- - {title} -
- -
-

- {value} -

- - {description} - -
-
- - ); -}; - -export default CardComponent; diff --git a/src/components/adminDashboard/TopProductsComponent.tsx b/src/components/adminDashboard/TopProductsComponent.tsx index c2444f180..418e533d0 100644 --- a/src/components/adminDashboard/TopProductsComponent.tsx +++ b/src/components/adminDashboard/TopProductsComponent.tsx @@ -1,5 +1,5 @@ -import Image from "next/image"; -import React from "react"; +import { ArrowUpRightIcon } from "lucide-react"; +import { FC } from "react"; import { Card } from "../ui/card"; @@ -13,21 +13,7 @@ type TopProductsProperties = { gradients: string[]; }; -const ExternalLinkIcon = () => { - return ( - <> - external link icon - - ); -}; - -const TopProductsComponent: React.FC = ({ +const TopProductsComponent: FC = ({ data, gradients, }) => { @@ -45,7 +31,7 @@ const TopProductsComponent: React.FC = ({
    diff --git a/src/components/adminDashboard/cardData.ts b/src/components/adminDashboard/cardData.ts index 96692068e..0ecfe1135 100644 --- a/src/components/adminDashboard/cardData.ts +++ b/src/components/adminDashboard/cardData.ts @@ -1,8 +1,10 @@ +import type { LucideIconName } from "~/components/common/lucide-icon"; + type CardData = { title: string; value: string; description: string; - icon: string; + icon: LucideIconName; }; export const cardData: CardData[] = [ @@ -10,24 +12,24 @@ export const cardData: CardData[] = [ title: "Total Revenue", value: "$45,000.00", description: "+20% from last month", - icon: `/admin-dashboard/icons/dollarSign.svg`, + icon: "dollar-sign", }, { title: "Total Users", value: "+4,000", description: "+10% from last month", - icon: `/admin-dashboard/icons/user.svg`, + icon: "user", }, { title: "Total Products", value: "1,000", description: "+20% from last month", - icon: `/admin-dashboard/icons/box.svg`, + icon: "box", }, { title: "Lifetime Sales", value: "$450,000.00", description: "+150% from last month", - icon: `/admin-dashboard/icons/arrowUp.svg`, + icon: "arrow-up-right", }, ]; diff --git a/src/components/common/DashboardCard/CardComponent.tsx b/src/components/common/DashboardCard/CardComponent.tsx new file mode 100644 index 000000000..4e108b042 --- /dev/null +++ b/src/components/common/DashboardCard/CardComponent.tsx @@ -0,0 +1,39 @@ +import { ComponentProps, FC } from "react"; + +import Icon from "~/components/common/lucide-icon"; + +interface CardProperties { + title: string; + value: string | number; + description: string; + icon: ComponentProps["name"]; +} + +const CardComponent: FC = ({ + title, + value, + description, + icon, +}) => { + return ( +
    +
    +

    + {title} +

    + +
    + +
    +

    + {value} +

    + + {description} + +
    +
    + ); +}; + +export default CardComponent; diff --git a/src/components/common/lucide-icon/index.tsx b/src/components/common/lucide-icon/index.tsx new file mode 100644 index 000000000..f07fbf21c --- /dev/null +++ b/src/components/common/lucide-icon/index.tsx @@ -0,0 +1,19 @@ +import { LucideProps } from "lucide-react"; +import dynamicIconImports from "lucide-react/dynamicIconImports"; +import dynamic from "next/dynamic"; + +interface ILucideIcon { + name: keyof typeof dynamicIconImports; +} + +export type LucideIconName = ILucideIcon["name"]; + +interface IconProperties extends Omit, ILucideIcon {} + +const Icon = ({ name, ...properties }: IconProperties) => { + const LucideIcon = dynamic(dynamicIconImports[name]); + + return ; +}; + +export default Icon; diff --git a/src/components/ui/calendar.tsx b/src/components/ui/calendar.tsx new file mode 100644 index 000000000..f73e53e23 --- /dev/null +++ b/src/components/ui/calendar.tsx @@ -0,0 +1,66 @@ +"use client"; + +import { ChevronLeft, ChevronRight } from "lucide-react"; +import * as React from "react"; +import { DayPicker } from "react-day-picker"; + +import { buttonVariants } from "~/components/common/common-button"; +import { cn } from "~/lib/utils"; + +export type CalendarProperties = React.ComponentProps; + +function Calendar({ + className, + classNames, + showOutsideDays = true, + ...properties +}: CalendarProperties) { + return ( + , + IconRight: () => , + }} + {...properties} + /> + ); +} +Calendar.displayName = "Calendar"; + +export { Calendar }; diff --git a/src/components/userDashboard/Chart.tsx b/src/components/userDashboard/Chart.tsx index 900aaa765..fe552232b 100644 --- a/src/components/userDashboard/Chart.tsx +++ b/src/components/userDashboard/Chart.tsx @@ -7,66 +7,72 @@ import { ChartTooltip, ChartTooltipContent, } from "~/components/ui/chart"; +import { cn } from "~/lib/utils"; type ChartProperties = { chartData: { month: string; revenue: number }[]; chartConfig: ChartConfig; chartTitle: string; + className?: string; }; export function Chart({ chartData = [], chartConfig, chartTitle, + className, }: ChartProperties) { return ( - <> - -

    - {chartTitle} -

    + +

    + {chartTitle} +

    -
    - - + + + - - - value.slice(0, 3)} - /> - `$${value}`} - /> - } - /> - - - - -
    -
    - + + value.slice(0, 3)} + /> + `$${value}`} + /> + } + /> + + + + +
+ ); }