From 1bc241ef8845beef6c7faf58113f4ed0b7d79868 Mon Sep 17 00:00:00 2001 From: Bhargav Ponnapalli Date: Sat, 13 Jul 2024 13:14:08 +0530 Subject: [PATCH] improvements to dashboard --- .../DashboardClientWrapper.tsx | 18 ++ .../_graphs/AreaChartInteractive.tsx | 262 ++++++++++++++++++ .../_graphs/BarChartActive.tsx | 108 ++++++++ .../_graphs/BarChartInteractive.tsx | 218 +++++++++++++++ .../_graphs/GraphContainer.tsx | 21 -- .../_graphs/OrganizationGraphs.tsx | 262 ++++-------------- .../_graphs/RadarChartGridCircleFilled.tsx | 75 +++++ .../_graphs/RadialChartGrid.tsx | 86 ++++++ .../(specific-organization-pages)/page.tsx | 111 +++----- .../(login-pages)/login/Login.tsx | 6 + src/components/AppAdminSidebarLink/index.tsx | 37 +-- src/components/GraphContainer.tsx | 8 +- src/components/Projects/ProjectsCardList.tsx | 106 +++++-- src/components/Search/Search.tsx | 5 +- src/styles/globals.css | 84 +++--- tailwind.config.cjs | 37 +-- 16 files changed, 1010 insertions(+), 434 deletions(-) create mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/DashboardClientWrapper.tsx create mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/AreaChartInteractive.tsx create mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/BarChartActive.tsx create mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/BarChartInteractive.tsx delete mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/GraphContainer.tsx create mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/RadarChartGridCircleFilled.tsx create mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/RadialChartGrid.tsx diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/DashboardClientWrapper.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/DashboardClientWrapper.tsx new file mode 100644 index 00000000..b04830aa --- /dev/null +++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/DashboardClientWrapper.tsx @@ -0,0 +1,18 @@ +'use client' + +import { motion } from "framer-motion"; + +export function DashboardClientWrapper({ + children +}: { + children: React.ReactNode; +}) { + return + {children} + +} diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/AreaChartInteractive.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/AreaChartInteractive.tsx new file mode 100644 index 00000000..0844c9c1 --- /dev/null +++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/AreaChartInteractive.tsx @@ -0,0 +1,262 @@ +"use client" + +import * as React from "react" +import { Area, AreaChart, CartesianGrid, XAxis } from "recharts" + +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card" +import { + ChartConfig, + ChartContainer, + ChartLegend, + ChartLegendContent, + ChartTooltip, + ChartTooltipContent, +} from "@/components/ui/chart" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" +const chartData = [ + { date: "2024-04-01", desktop: 222, mobile: 150 }, + { date: "2024-04-02", desktop: 97, mobile: 180 }, + { date: "2024-04-03", desktop: 167, mobile: 120 }, + { date: "2024-04-04", desktop: 242, mobile: 260 }, + { date: "2024-04-05", desktop: 373, mobile: 290 }, + { date: "2024-04-06", desktop: 301, mobile: 340 }, + { date: "2024-04-07", desktop: 245, mobile: 180 }, + { date: "2024-04-08", desktop: 409, mobile: 320 }, + { date: "2024-04-09", desktop: 59, mobile: 110 }, + { date: "2024-04-10", desktop: 261, mobile: 190 }, + { date: "2024-04-11", desktop: 327, mobile: 350 }, + { date: "2024-04-12", desktop: 292, mobile: 210 }, + { date: "2024-04-13", desktop: 342, mobile: 380 }, + { date: "2024-04-14", desktop: 137, mobile: 220 }, + { date: "2024-04-15", desktop: 120, mobile: 170 }, + { date: "2024-04-16", desktop: 138, mobile: 190 }, + { date: "2024-04-17", desktop: 446, mobile: 360 }, + { date: "2024-04-18", desktop: 364, mobile: 410 }, + { date: "2024-04-19", desktop: 243, mobile: 180 }, + { date: "2024-04-20", desktop: 89, mobile: 150 }, + { date: "2024-04-21", desktop: 137, mobile: 200 }, + { date: "2024-04-22", desktop: 224, mobile: 170 }, + { date: "2024-04-23", desktop: 138, mobile: 230 }, + { date: "2024-04-24", desktop: 387, mobile: 290 }, + { date: "2024-04-25", desktop: 215, mobile: 250 }, + { date: "2024-04-26", desktop: 75, mobile: 130 }, + { date: "2024-04-27", desktop: 383, mobile: 420 }, + { date: "2024-04-28", desktop: 122, mobile: 180 }, + { date: "2024-04-29", desktop: 315, mobile: 240 }, + { date: "2024-04-30", desktop: 454, mobile: 380 }, + { date: "2024-05-01", desktop: 165, mobile: 220 }, + { date: "2024-05-02", desktop: 293, mobile: 310 }, + { date: "2024-05-03", desktop: 247, mobile: 190 }, + { date: "2024-05-04", desktop: 385, mobile: 420 }, + { date: "2024-05-05", desktop: 481, mobile: 390 }, + { date: "2024-05-06", desktop: 498, mobile: 520 }, + { date: "2024-05-07", desktop: 388, mobile: 300 }, + { date: "2024-05-08", desktop: 149, mobile: 210 }, + { date: "2024-05-09", desktop: 227, mobile: 180 }, + { date: "2024-05-10", desktop: 293, mobile: 330 }, + { date: "2024-05-11", desktop: 335, mobile: 270 }, + { date: "2024-05-12", desktop: 197, mobile: 240 }, + { date: "2024-05-13", desktop: 197, mobile: 160 }, + { date: "2024-05-14", desktop: 448, mobile: 490 }, + { date: "2024-05-15", desktop: 473, mobile: 380 }, + { date: "2024-05-16", desktop: 338, mobile: 400 }, + { date: "2024-05-17", desktop: 499, mobile: 420 }, + { date: "2024-05-18", desktop: 315, mobile: 350 }, + { date: "2024-05-19", desktop: 235, mobile: 180 }, + { date: "2024-05-20", desktop: 177, mobile: 230 }, + { date: "2024-05-21", desktop: 82, mobile: 140 }, + { date: "2024-05-22", desktop: 81, mobile: 120 }, + { date: "2024-05-23", desktop: 252, mobile: 290 }, + { date: "2024-05-24", desktop: 294, mobile: 220 }, + { date: "2024-05-25", desktop: 201, mobile: 250 }, + { date: "2024-05-26", desktop: 213, mobile: 170 }, + { date: "2024-05-27", desktop: 420, mobile: 460 }, + { date: "2024-05-28", desktop: 233, mobile: 190 }, + { date: "2024-05-29", desktop: 78, mobile: 130 }, + { date: "2024-05-30", desktop: 340, mobile: 280 }, + { date: "2024-05-31", desktop: 178, mobile: 230 }, + { date: "2024-06-01", desktop: 178, mobile: 200 }, + { date: "2024-06-02", desktop: 470, mobile: 410 }, + { date: "2024-06-03", desktop: 103, mobile: 160 }, + { date: "2024-06-04", desktop: 439, mobile: 380 }, + { date: "2024-06-05", desktop: 88, mobile: 140 }, + { date: "2024-06-06", desktop: 294, mobile: 250 }, + { date: "2024-06-07", desktop: 323, mobile: 370 }, + { date: "2024-06-08", desktop: 385, mobile: 320 }, + { date: "2024-06-09", desktop: 438, mobile: 480 }, + { date: "2024-06-10", desktop: 155, mobile: 200 }, + { date: "2024-06-11", desktop: 92, mobile: 150 }, + { date: "2024-06-12", desktop: 492, mobile: 420 }, + { date: "2024-06-13", desktop: 81, mobile: 130 }, + { date: "2024-06-14", desktop: 426, mobile: 380 }, + { date: "2024-06-15", desktop: 307, mobile: 350 }, + { date: "2024-06-16", desktop: 371, mobile: 310 }, + { date: "2024-06-17", desktop: 475, mobile: 520 }, + { date: "2024-06-18", desktop: 107, mobile: 170 }, + { date: "2024-06-19", desktop: 341, mobile: 290 }, + { date: "2024-06-20", desktop: 408, mobile: 450 }, + { date: "2024-06-21", desktop: 169, mobile: 210 }, + { date: "2024-06-22", desktop: 317, mobile: 270 }, + { date: "2024-06-23", desktop: 480, mobile: 530 }, + { date: "2024-06-24", desktop: 132, mobile: 180 }, + { date: "2024-06-25", desktop: 141, mobile: 190 }, + { date: "2024-06-26", desktop: 434, mobile: 380 }, + { date: "2024-06-27", desktop: 448, mobile: 490 }, + { date: "2024-06-28", desktop: 149, mobile: 200 }, + { date: "2024-06-29", desktop: 103, mobile: 160 }, + { date: "2024-06-30", desktop: 446, mobile: 400 }, +] + +const chartConfig = { + visitors: { + label: "Visitors", + }, + desktop: { + label: "Desktop", + color: "hsl(var(--chart-1))", + }, + mobile: { + label: "Mobile", + color: "hsl(var(--chart-2))", + }, +} satisfies ChartConfig + +export function AreaChartInteractiveLarge() { + const [timeRange, setTimeRange] = React.useState("90d") + + const filteredData = chartData.filter((item) => { + const date = new Date(item.date) + const now = new Date() + let daysToSubtract = 90 + if (timeRange === "30d") { + daysToSubtract = 30 + } else if (timeRange === "7d") { + daysToSubtract = 7 + } + now.setDate(now.getDate() - daysToSubtract) + return date >= now + }) + + return ( + + +
+ Page Views - Interactive + + Showing total visitors for the last 3 months + +
+ +
+ + + + + + + + + + + + + + + { + const date = new Date(value) + return date.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }) + }} + /> + { + return new Date(value).toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }) + }} + indicator="dot" + /> + } + /> + + + } /> + + + +
+ ) +} diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/BarChartActive.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/BarChartActive.tsx new file mode 100644 index 00000000..0302dfcd --- /dev/null +++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/BarChartActive.tsx @@ -0,0 +1,108 @@ +"use client" + +import { TrendingUp } from "lucide-react" +import { Bar, BarChart, CartesianGrid, Rectangle, XAxis } from "recharts" + +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card" +import { + ChartConfig, + ChartContainer, + ChartTooltip, + ChartTooltipContent, +} from "@/components/ui/chart" +const chartData = [ + { browser: "chrome", visitors: 187, fill: "var(--color-chrome)" }, + { browser: "safari", visitors: 200, fill: "var(--color-safari)" }, + { browser: "firefox", visitors: 275, fill: "var(--color-firefox)" }, + { browser: "edge", visitors: 173, fill: "var(--color-edge)" }, + { browser: "other", visitors: 90, fill: "var(--color-other)" }, +] + +const chartConfig = { + visitors: { + label: "Visitors", + }, + chrome: { + label: "Chrome", + color: "hsl(var(--chart-1))", + }, + safari: { + label: "Safari", + color: "hsl(var(--chart-2))", + }, + firefox: { + label: "Firefox", + color: "hsl(var(--chart-3))", + }, + edge: { + label: "Edge", + color: "hsl(var(--chart-4))", + }, + other: { + label: "Other", + color: "hsl(var(--chart-5))", + }, +} satisfies ChartConfig + +export function BarChartActive() { + return ( + + + Bar Chart - Active + January - June 2024 + + + + + + + chartConfig[value as keyof typeof chartConfig]?.label + } + /> + } + /> + { + return ( + + ) + }} + /> + + + + +
+ Trending up by 5.2% this month +
+
+ Showing total visitors for the last 6 months +
+
+
+ ) +} diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/BarChartInteractive.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/BarChartInteractive.tsx new file mode 100644 index 00000000..5b2c582a --- /dev/null +++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/BarChartInteractive.tsx @@ -0,0 +1,218 @@ +"use client" + +import * as React from "react" +import { Bar, BarChart, CartesianGrid, XAxis } from "recharts" + +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card" +import { + ChartConfig, + ChartContainer, + ChartTooltip, + ChartTooltipContent, +} from "@/components/ui/chart" +const chartData = [ + { date: "2024-04-01", desktop: 222, mobile: 150 }, + { date: "2024-04-02", desktop: 97, mobile: 180 }, + { date: "2024-04-03", desktop: 167, mobile: 120 }, + { date: "2024-04-04", desktop: 242, mobile: 260 }, + { date: "2024-04-05", desktop: 373, mobile: 290 }, + { date: "2024-04-06", desktop: 301, mobile: 340 }, + { date: "2024-04-07", desktop: 245, mobile: 180 }, + { date: "2024-04-08", desktop: 409, mobile: 320 }, + { date: "2024-04-09", desktop: 59, mobile: 110 }, + { date: "2024-04-10", desktop: 261, mobile: 190 }, + { date: "2024-04-11", desktop: 327, mobile: 350 }, + { date: "2024-04-12", desktop: 292, mobile: 210 }, + { date: "2024-04-13", desktop: 342, mobile: 380 }, + { date: "2024-04-14", desktop: 137, mobile: 220 }, + { date: "2024-04-15", desktop: 120, mobile: 170 }, + { date: "2024-04-16", desktop: 138, mobile: 190 }, + { date: "2024-04-17", desktop: 446, mobile: 360 }, + { date: "2024-04-18", desktop: 364, mobile: 410 }, + { date: "2024-04-19", desktop: 243, mobile: 180 }, + { date: "2024-04-20", desktop: 89, mobile: 150 }, + { date: "2024-04-21", desktop: 137, mobile: 200 }, + { date: "2024-04-22", desktop: 224, mobile: 170 }, + { date: "2024-04-23", desktop: 138, mobile: 230 }, + { date: "2024-04-24", desktop: 387, mobile: 290 }, + { date: "2024-04-25", desktop: 215, mobile: 250 }, + { date: "2024-04-26", desktop: 75, mobile: 130 }, + { date: "2024-04-27", desktop: 383, mobile: 420 }, + { date: "2024-04-28", desktop: 122, mobile: 180 }, + { date: "2024-04-29", desktop: 315, mobile: 240 }, + { date: "2024-04-30", desktop: 454, mobile: 380 }, + { date: "2024-05-01", desktop: 165, mobile: 220 }, + { date: "2024-05-02", desktop: 293, mobile: 310 }, + { date: "2024-05-03", desktop: 247, mobile: 190 }, + { date: "2024-05-04", desktop: 385, mobile: 420 }, + { date: "2024-05-05", desktop: 481, mobile: 390 }, + { date: "2024-05-06", desktop: 498, mobile: 520 }, + { date: "2024-05-07", desktop: 388, mobile: 300 }, + { date: "2024-05-08", desktop: 149, mobile: 210 }, + { date: "2024-05-09", desktop: 227, mobile: 180 }, + { date: "2024-05-10", desktop: 293, mobile: 330 }, + { date: "2024-05-11", desktop: 335, mobile: 270 }, + { date: "2024-05-12", desktop: 197, mobile: 240 }, + { date: "2024-05-13", desktop: 197, mobile: 160 }, + { date: "2024-05-14", desktop: 448, mobile: 490 }, + { date: "2024-05-15", desktop: 473, mobile: 380 }, + { date: "2024-05-16", desktop: 338, mobile: 400 }, + { date: "2024-05-17", desktop: 499, mobile: 420 }, + { date: "2024-05-18", desktop: 315, mobile: 350 }, + { date: "2024-05-19", desktop: 235, mobile: 180 }, + { date: "2024-05-20", desktop: 177, mobile: 230 }, + { date: "2024-05-21", desktop: 82, mobile: 140 }, + { date: "2024-05-22", desktop: 81, mobile: 120 }, + { date: "2024-05-23", desktop: 252, mobile: 290 }, + { date: "2024-05-24", desktop: 294, mobile: 220 }, + { date: "2024-05-25", desktop: 201, mobile: 250 }, + { date: "2024-05-26", desktop: 213, mobile: 170 }, + { date: "2024-05-27", desktop: 420, mobile: 460 }, + { date: "2024-05-28", desktop: 233, mobile: 190 }, + { date: "2024-05-29", desktop: 78, mobile: 130 }, + { date: "2024-05-30", desktop: 340, mobile: 280 }, + { date: "2024-05-31", desktop: 178, mobile: 230 }, + { date: "2024-06-01", desktop: 178, mobile: 200 }, + { date: "2024-06-02", desktop: 470, mobile: 410 }, + { date: "2024-06-03", desktop: 103, mobile: 160 }, + { date: "2024-06-04", desktop: 439, mobile: 380 }, + { date: "2024-06-05", desktop: 88, mobile: 140 }, + { date: "2024-06-06", desktop: 294, mobile: 250 }, + { date: "2024-06-07", desktop: 323, mobile: 370 }, + { date: "2024-06-08", desktop: 385, mobile: 320 }, + { date: "2024-06-09", desktop: 438, mobile: 480 }, + { date: "2024-06-10", desktop: 155, mobile: 200 }, + { date: "2024-06-11", desktop: 92, mobile: 150 }, + { date: "2024-06-12", desktop: 492, mobile: 420 }, + { date: "2024-06-13", desktop: 81, mobile: 130 }, + { date: "2024-06-14", desktop: 426, mobile: 380 }, + { date: "2024-06-15", desktop: 307, mobile: 350 }, + { date: "2024-06-16", desktop: 371, mobile: 310 }, + { date: "2024-06-17", desktop: 475, mobile: 520 }, + { date: "2024-06-18", desktop: 107, mobile: 170 }, + { date: "2024-06-19", desktop: 341, mobile: 290 }, + { date: "2024-06-20", desktop: 408, mobile: 450 }, + { date: "2024-06-21", desktop: 169, mobile: 210 }, + { date: "2024-06-22", desktop: 317, mobile: 270 }, + { date: "2024-06-23", desktop: 480, mobile: 530 }, + { date: "2024-06-24", desktop: 132, mobile: 180 }, + { date: "2024-06-25", desktop: 141, mobile: 190 }, + { date: "2024-06-26", desktop: 434, mobile: 380 }, + { date: "2024-06-27", desktop: 448, mobile: 490 }, + { date: "2024-06-28", desktop: 149, mobile: 200 }, + { date: "2024-06-29", desktop: 103, mobile: 160 }, + { date: "2024-06-30", desktop: 446, mobile: 400 }, +] + +const chartConfig = { + views: { + label: "Page Views", + }, + desktop: { + label: "Desktop", + color: "hsl(var(--chart-1))", + }, + mobile: { + label: "Mobile", + color: "hsl(var(--chart-2))", + }, +} satisfies ChartConfig + +export function BarChartInteractive() { + const [activeChart, setActiveChart] = + React.useState("desktop") + + const total = React.useMemo( + () => ({ + desktop: chartData.reduce((acc, curr) => acc + curr.desktop, 0), + mobile: chartData.reduce((acc, curr) => acc + curr.mobile, 0), + }), + [] + ) + + return ( + + +
+ Bar Chart - Interactive + + Showing total visitors for the last 3 months + +
+
+ {["desktop", "mobile"].map((key) => { + const chart = key as keyof typeof chartConfig + return ( + + ) + })} +
+
+ + + + + { + const date = new Date(value) + return date.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }) + }} + /> + { + return new Date(value).toLocaleDateString("en-US", { + month: "short", + day: "numeric", + year: "numeric", + }) + }} + /> + } + /> + + + + +
+ ) +} diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/GraphContainer.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/GraphContainer.tsx deleted file mode 100644 index d087e5c5..00000000 --- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/GraphContainer.tsx +++ /dev/null @@ -1,21 +0,0 @@ -"use client" - -import dynamic from 'next/dynamic'; - -const OrganizationGraphs = dynamic(() => import('./OrganizationGraphs').then(m => m.OrganizationGraphs), { - ssr: false -}); - -export function GraphContainer({ - children, - organizationSlug -}: { - children: React.ReactNode; - organizationSlug: string; -}) { - - return {children} - -} diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/OrganizationGraphs.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/OrganizationGraphs.tsx index a4f262c5..269df125 100644 --- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/OrganizationGraphs.tsx +++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/OrganizationGraphs.tsx @@ -1,215 +1,61 @@ -"use client"; - -import { AreaChartComponent } from "@/components/AreaChart"; -import { BarChartComponent } from "@/components/BarChart"; -import { DonutChartComponent } from "@/components/DonutChart"; -import { GraphContainer } from "@/components/GraphContainer"; -import { PageHeading } from "@/components/PageHeading"; -import { Button } from "@/components/ui/button"; -import Link from "next/link"; - -const chartDataForMonth = [ - { - name: "January", - value: 2890, - }, - { - name: "February", - value: 2756, - }, - { - name: "March", - value: 3322, - }, - { - name: "April", - value: 3470, - }, - { - name: "May", - value: 3475, - }, - { - name: "June", - value: 3129, - }, - { - name: "July", - value: 3482, - }, - { - name: "August", - value: 2412, - }, - { - name: "September", - value: 2678, - }, - { - name: "October", - value: 2190, - }, - { - name: "November", - value: 2498, - }, - { - name: "December", - value: 2598, - }, -]; - -const chartDataForWeek = [ - { - name: "1", - value: 2890, - fill: "hsl(var(--chart-1))" - }, - { - name: "2", - value: 2756, - fill: "hsl(var(--chart-3))" - }, - { - name: "3", - value: 3322, - fill: "hsl(var(--chart-4))" - }, - { - name: "4", - value: 3470, - fill: "hsl(var(--chart-5))" - }, -]; - -const chartDataForYears = [ - { - name: "January", - value: 1500, - }, - { - name: "February", - value: 4000, - }, - { - name: "March", - value: 2800, - }, - { - name: "April", - value: 2500, - }, - { - name: "May", - value: 3400, - }, - { - name: "June", - value: 3200, - }, - { - name: "July", - value: 2300, - }, - { - name: "August", - value: 2500, - }, - { - name: "September", - value: 2800, - }, - { - name: "October", - value: 3000, - }, - { - name: "November", - value: 4200, - }, - { - name: "December", - value: 4800, - }, -]; - -const calculateBadgeValue = (data) => { - const lastMonth = data[data.length - 1].value; - const secondLastMonth = data[data.length - 2].value; - const percentageChange = - ((lastMonth - secondLastMonth) / secondLastMonth) * 100; - return { - badgeValue: `${percentageChange.toFixed(2)}%`, - }; -}; - -const { badgeValue: badgeValueForMonth } = - calculateBadgeValue(chartDataForMonth); - -const { badgeValue: badgeValueforWeek } = calculateBadgeValue(chartDataForWeek); -const { badgeValue: badgeValueForYear } = - calculateBadgeValue(chartDataForYears); - -const dataFormatter = (number: number) => { - return `$ ${Intl.NumberFormat("us").format(number).toString()}`; +'use client' +import { motion } from "framer-motion"; +import { AreaChartInteractiveLarge } from "./AreaChartInteractive"; +import { BarChartActive } from "./BarChartActive"; +import { BarChartInteractive } from "./BarChartInteractive"; +import { RadarChartGridCircleFilled } from "./RadarChartGridCircleFilled"; +import { RadialChartGrid } from "./RadialChartGrid"; + +const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1 + } + } }; -const valueFormatter = (number: number) => - `$ ${Intl.NumberFormat("us").format(number).toString()}`; - -type Props = { - organizationSlug: string; - children: React.ReactNode; +const itemVariants = { + hidden: { y: 20, opacity: 0 }, + visible: { + y: 0, + opacity: 1, + transition: { + type: "spring", + stiffness: 100 + } + } }; -export function OrganizationGraphs({ organizationSlug, children }: Props) { +export function OrganizationGraphs() { return ( - <> -
- - - - - - -
- - - - -
- -
-
- - -
- - {children} -
- -
-
-
- + + + + + + + + + + + + + + + + + + + ); } - diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/RadarChartGridCircleFilled.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/RadarChartGridCircleFilled.tsx new file mode 100644 index 00000000..25418ca6 --- /dev/null +++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/RadarChartGridCircleFilled.tsx @@ -0,0 +1,75 @@ +"use client" + +import { TrendingUp } from "lucide-react" +import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts" + +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card" +import { + ChartConfig, + ChartContainer, + ChartTooltip, + ChartTooltipContent, +} from "@/components/ui/chart" +const chartData = [ + { month: "January", desktop: 186 }, + { month: "February", desktop: 285 }, + { month: "March", desktop: 237 }, + { month: "April", desktop: 203 }, + { month: "May", desktop: 209 }, + { month: "June", desktop: 264 }, +] + +const chartConfig = { + desktop: { + label: "Desktop", + color: "hsl(var(--chart-1))", + }, +} satisfies ChartConfig + +export function RadarChartGridCircleFilled() { + return ( + + + Radar Chart - Grid Circle Filled + + Showing total visitors for the last 6 months + + + + + + } /> + + + + + + + +
+ Trending up by 5.2% this month +
+
+ January - June 2024 +
+
+
+ ) +} diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/RadialChartGrid.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/RadialChartGrid.tsx new file mode 100644 index 00000000..a31a6d3e --- /dev/null +++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/_graphs/RadialChartGrid.tsx @@ -0,0 +1,86 @@ +"use client" + +import { TrendingUp } from "lucide-react" +import { PolarGrid, RadialBar, RadialBarChart } from "recharts" + +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card" +import { + ChartConfig, + ChartContainer, + ChartTooltip, + ChartTooltipContent, +} from "@/components/ui/chart" +const chartData = [ + { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" }, + { browser: "safari", visitors: 200, fill: "var(--color-safari)" }, + { browser: "firefox", visitors: 187, fill: "var(--color-firefox)" }, + { browser: "edge", visitors: 173, fill: "var(--color-edge)" }, + { browser: "other", visitors: 90, fill: "var(--color-other)" }, +] + +const chartConfig = { + visitors: { + label: "Visitors", + }, + chrome: { + label: "Chrome", + color: "hsl(var(--chart-1))", + }, + safari: { + label: "Safari", + color: "hsl(var(--chart-2))", + }, + firefox: { + label: "Firefox", + color: "hsl(var(--chart-3))", + }, + edge: { + label: "Edge", + color: "hsl(var(--chart-4))", + }, + other: { + label: "Other", + color: "hsl(var(--chart-5))", + }, +} satisfies ChartConfig + +export function RadialChartGrid() { + return ( + + + Radial Chart - Grid + January - June 2024 + + + + + } + /> + + + + + + +
+ Trending up by 5.2% this month +
+
+ Showing total visitors for the last 6 months +
+
+
+ ) +} diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/page.tsx index 31641522..5051908a 100644 --- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/page.tsx +++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/[organizationSlug]/(specific-organization-pages)/page.tsx @@ -2,28 +2,30 @@ import { CreateProjectDialog } from "@/components/CreateProjectDialog"; import { ProjectsCardList } from "@/components/Projects/ProjectsCardList"; import { Search } from "@/components/Search"; import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { getOrganizationIdBySlug, getOrganizationTitle } from "@/data/user/organizations"; import { getProjects } from "@/data/user/projects"; import { organizationSlugParamSchema, projectsfilterSchema, } from "@/utils/zod-schemas/params"; -import { Layers } from "lucide-react"; +import { FileText, Layers } from "lucide-react"; import type { Metadata } from 'next'; import Link from "next/link"; import { Suspense } from "react"; import type { z } from "zod"; +import { DashboardClientWrapper } from "./DashboardClientWrapper"; import { DashboardLoadingFallback } from "./DashboardLoadingFallback"; import ProjectsLoadingFallback from "./ProjectsLoadingFallback"; -import { TeamMembers } from "./TeamMembers"; -import { ExportPDF } from "./_exportPdf/ExportPdf"; -import { GraphContainer } from "./_graphs/GraphContainer"; - +import { OrganizationGraphs } from "./_graphs/OrganizationGraphs"; async function Projects({ organizationId, filters, -}: { organizationId: string; filters: z.infer }) { +}: { + organizationId: string; + filters: z.infer; +}) { const projects = await getProjects({ organizationId, ...filters, @@ -36,79 +38,57 @@ export type DashboardProps = { searchParams: unknown; }; -async function Dashboard({ - params, - searchParams, -}: DashboardProps) { +async function Dashboard({ params, searchParams }: DashboardProps) { const { organizationSlug } = organizationSlugParamSchema.parse(params); const organizationId = await getOrganizationIdBySlug(organizationSlug); const validatedSearchParams = projectsfilterSchema.parse(searchParams); return ( -
-
-
-
-

Dashboard

-
- - -
+ + + + Dashboard +
+ +
-
-

Recent Projects

-
- -
- - -
- }> - - {validatedSearchParams.query && ( -

- Searching for{" "} - - {validatedSearchParams.query} - -

- )} -
-
-
-
-
- - Loading...
}> - + }> + + {validatedSearchParams.query && ( +

+ Searching for{" "} + {validatedSearchParams.query} +

+ )}
- -
-
+ + + + ); } - -export async function generateMetadata( - { - params, - }: DashboardProps -): Promise { - +export async function generateMetadata({ params }: DashboardProps): Promise { const { organizationSlug } = organizationSlugParamSchema.parse(params); const organizationId = await getOrganizationIdBySlug(organizationSlug); const title = await getOrganizationTitle(organizationId); @@ -119,10 +99,7 @@ export async function generateMetadata( }; } -export default async function OrganizationPage({ - params, - searchParams, -}: DashboardProps) { +export default async function OrganizationPage({ params, searchParams }: DashboardProps) { return ( }> diff --git a/src/app/(dynamic-pages)/(login-pages)/login/Login.tsx b/src/app/(dynamic-pages)/(login-pages)/login/Login.tsx index 97ec2a7b..5087186f 100644 --- a/src/app/(dynamic-pages)/(login-pages)/login/Login.tsx +++ b/src/app/(dynamic-pages)/(login-pages)/login/Login.tsx @@ -21,6 +21,7 @@ import { useSAToastMutation } from '@/hooks/useSAToastMutation'; import type { AuthProvider } from '@/types'; import { useRouter } from 'next/navigation'; import { useState } from 'react'; +import { useDidMount } from 'rooks'; export function Login({ next, @@ -34,6 +35,11 @@ export function Login({ const router = useRouter(); + useDidMount(() => { + console.log('prefetching dashboard') + router.prefetch('/dashboard') + }) + function redirectToDashboard() { router.refresh(); if (next) { diff --git a/src/components/AppAdminSidebarLink/index.tsx b/src/components/AppAdminSidebarLink/index.tsx index 1356d420..81f6e02a 100644 --- a/src/components/AppAdminSidebarLink/index.tsx +++ b/src/components/AppAdminSidebarLink/index.tsx @@ -1,3 +1,4 @@ +import { Button } from '@/components/ui/button'; import { getIsAppAdmin } from '@/data/user/user'; import { cn } from '@/utils/cn'; import { Server } from 'lucide-react'; @@ -5,29 +6,21 @@ import Link from 'next/link'; export async function AppAdminSidebarLink() { const isUserAppAdmin = await getIsAppAdmin(); + return ( - <> - {isUserAppAdmin ? ( - - Admin Panel - - ) : ( - - Admin Panel Preview - + + ); } diff --git a/src/components/GraphContainer.tsx b/src/components/GraphContainer.tsx index ba22f463..3079ba91 100644 --- a/src/components/GraphContainer.tsx +++ b/src/components/GraphContainer.tsx @@ -32,11 +32,11 @@ export function GraphContainer({ badgeValue, }: GraphContainerProps) { return ( -
+
- {title} - {subTitle} + {title} + {subTitle}
{badgeValue && (
-
{children}
+
{children}
); diff --git a/src/components/Projects/ProjectsCardList.tsx b/src/components/Projects/ProjectsCardList.tsx index caec5f92..4debad38 100644 --- a/src/components/Projects/ProjectsCardList.tsx +++ b/src/components/Projects/ProjectsCardList.tsx @@ -1,11 +1,13 @@ "use client"; -import { T } from "@/components/ui/Typography"; -import type { Table } from "@/types"; + +import { Badge } from "@/components/ui/badge"; +import { Card, CardContent } from "@/components/ui/card"; +import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"; +import { Table } from "@/types"; import { format } from "date-fns"; -import { CalendarDays } from "lucide-react"; +import { motion } from "framer-motion"; +import { CalendarDays, Clock, Link as LinkIcon } from "lucide-react"; import Link from "next/link"; -import { ProjectBadge } from "../ui/badge-project"; -import { Card, CardContent, CardFooter, CardHeader } from "../ui/card"; export enum ProjectStatus { draft = "draft", @@ -14,6 +16,30 @@ export enum ProjectStatus { completed = "completed", } +const statusEmojis = { + draft: "📝", + pending_approval: "⏳", + approved: "🏗️", + completed: "✅", +}; + +const MotionCard = motion(Card); +const MotionCardContent = motion(CardContent); + +const cardVariants = { + hidden: { opacity: 0, y: 20 }, + visible: { opacity: 1, y: 0 }, +}; + +const contentVariants = { + hidden: { opacity: 0 }, + visible: { opacity: 1, transition: { staggerChildren: 0.1 } }, +}; + +const itemVariants = { + hidden: { opacity: 0, y: 10 }, + visible: { opacity: 1, y: 0 }, +}; export const ProjectsCardList = ({ projects, @@ -22,33 +48,55 @@ export const ProjectsCardList = ({ }) => { if (projects.length === 0) { return ( - +

🔍 No matching projects found. - +

); } + return ( -
- {projects.slice(0, 5).map((project) => ( - - - -
- {ProjectStatus[project.project_status]} -
-
- -

{project.name}

-
- -
- - {format(new Date(project.created_at), "dd MMMM, yyyy")} -
-
-
- - ))} -
+ +
+ {projects.slice(0, 5).map((project, index) => ( + + + + + + {statusEmojis[project.project_status]} {capitalizeFirstLetter(project.project_status)} + + ID: {project.id.slice(0, 7)} + + {project.name} + + + Created: {format(new Date(project.created_at), "dd MMM yyyy")} + + + + Updated: {format(new Date(project.updated_at), "dd MMM yyyy")} + + + + /{project.slug} + + + + + ))} +
+ +
); }; + +function capitalizeFirstLetter(string: string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} diff --git a/src/components/Search/Search.tsx b/src/components/Search/Search.tsx index 36b01a19..1b6522af 100644 --- a/src/components/Search/Search.tsx +++ b/src/components/Search/Search.tsx @@ -1,10 +1,11 @@ 'use client'; import { Input } from '@/components/ui/input'; +import { cn } from '@/lib/utils'; import { usePathname, useRouter, useSearchParams } from 'next/navigation'; import { useDebouncedCallback } from 'use-debounce'; -export function Search({ placeholder }: { placeholder: string }) { +export function Search({ placeholder, className }: { placeholder: string, className?: string }) { const searchParams = useSearchParams(); const { replace } = useRouter(); const pathname = usePathname(); @@ -25,7 +26,7 @@ export function Search({ placeholder }: { placeholder: string }) { }, 300); return ( -
+
diff --git a/src/styles/globals.css b/src/styles/globals.css index 87acbb46..a8a3233b 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -5,65 +5,57 @@ @layer base { :root { --background: 0 0% 100%; - --foreground: 222.2 84% 4.9%; + --foreground: 240 10% 3.9%; --card: 0 0% 100%; - --card-foreground: 222.2 84% 4.9%; + --card-foreground: 240 10% 3.9%; --popover: 0 0% 100%; - --popover-foreground: 222.2 84% 4.9%; - --primary: 222.2 47.4% 11.2%; - --primary-foreground: 210 40% 98%; - --secondary: 210 40% 96.1%; - --secondary-foreground: 222.2 47.4% 11.2%; - --muted: 210 40% 96.1%; - --muted-foreground: 215.4 16.3% 46.9%; - --accent: 210 40% 96.1%; - --accent-foreground: 222.2 47.4% 11.2%; + --popover-foreground: 240 10% 3.9%; + --primary: 240 5.9% 10%; + --primary-foreground: 0 0% 98%; + --secondary: 240 4.8% 95.9%; + --secondary-foreground: 240 5.9% 10%; + --muted: 240 4.8% 95.9%; + --muted-foreground: 240 3.8% 46.1%; + --accent: 240 4.8% 95.9%; + --accent-foreground: 240 5.9% 10%; --destructive: 0 84.2% 60.2%; - --destructive-foreground: 210 40% 98%; - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; - --ring: 222.2 84% 4.9%; + --destructive-foreground: 0 0% 98%; + --border: 240 5.9% 90%; + --input: 240 5.9% 90%; + --ring: 240 10% 3.9%; --radius: 0.5rem; - } - - .dark { - --background: 222.2 84% 4.9%; - --foreground: 210 40% 98%; - --card: 222.2 84% 4.9%; - --card-foreground: 210 40% 98%; - --popover: 222.2 84% 4.9%; - --popover-foreground: 210 40% 98%; - --primary: 210 40% 98%; - --primary-foreground: 222.2 47.4% 11.2%; - --secondary: 217.2 32.6% 17.5%; - --secondary-foreground: 210 40% 98%; - --muted: 217.2 32.6% 17.5%; - --muted-foreground: 215 20.2% 65.1%; - --accent: 217.2 32.6% 17.5%; - --accent-foreground: 210 40% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 210 40% 98%; - --border: 217.2 32.6% 17.5%; - --input: 217.2 32.6% 17.5%; - --ring: 212.7 26.8% 83.9; - } -} - -@layer base { - :root { - --chart-1: 12 76% 61%; - --chart-2: 173 58% 39%; + --chart-1: 173 58% 39%; + --chart-2: 12 76% 61%; --chart-3: 197 37% 24%; --chart-4: 43 74% 66%; --chart-5: 27 87% 67%; } .dark { + --background: 240 10% 3.9%; + --foreground: 0 0% 98%; + --card: 240 10% 3.9%; + --card-foreground: 0 0% 98%; + --popover: 240 10% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 0 0% 98%; + --primary-foreground: 240 5.9% 10%; + --secondary: 240 3.7% 15.9%; + --secondary-foreground: 0 0% 98%; + --muted: 240 3.7% 15.9%; + --muted-foreground: 240 5% 64.9%; + --accent: 240 3.7% 15.9%; + --accent-foreground: 0 0% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + --border: 240 3.7% 15.9%; + --input: 240 3.7% 15.9%; + --ring: 240 4.9% 83.9%; --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; + --chart-5: 160 60% 45%; --chart-3: 30 80% 55%; --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; + --chart-2: 340 75% 55%; } } diff --git a/tailwind.config.cjs b/tailwind.config.cjs index 4d5e9d43..897db6c0 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -3,11 +3,7 @@ const defaultTheme = require('tailwindcss/defaultTheme'); module.exports = { darkMode: ['class'], - content: [ - './src/**/*.{js,ts,jsx,tsx,mdx}', - './node_modules/@tremor/**/*.{js,ts,jsx,tsx,mdx}', - ], - + content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'], prefix: '', theme: { transparent: 'transparent', @@ -99,39 +95,10 @@ module.exports = { }, }, }, - safelist: [ - { - pattern: - /^(bg-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, - variants: ['hover', 'ui-selected'], - }, - { - pattern: - /^(text-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, - variants: ['hover', 'ui-selected'], - }, - { - pattern: - /^(border-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, - variants: ['hover', 'ui-selected'], - }, - { - pattern: - /^(ring-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, - }, - { - pattern: - /^(stroke-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, - }, - { - pattern: - /^(fill-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, - }, - ], + plugins: [ require('tailwindcss-animate'), require('@tailwindcss/typography'), - require('@headlessui/tailwindcss'), require('@tailwindcss/forms'), ], };