diff --git a/package.json b/package.json index f616276..9c6c2c0 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1", + "@radix-ui/react-hover-card": "^1.1.1", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-popover": "^1.1.1", @@ -30,6 +31,7 @@ "@tanstack/react-table": "^8.20.1", "@types/cookie": "^0.6.0", "@types/qs": "^6.9.15", + "@types/react-calendar-heatmap": "^1.6.7", "axios": "^1.7.4", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", @@ -43,6 +45,7 @@ "pg": "^8.12.0", "qs": "^6.13.0", "react": "^18", + "react-calendar-heatmap": "^1.9.0", "react-day-picker": "9.0.8", "react-dom": "^18", "react-grid-gallery": "^1.0.1", diff --git a/src/components/analytics/exif/contributiongraph.tsx b/src/components/analytics/exif/contributiongraph.tsx new file mode 100644 index 0000000..59a6a26 --- /dev/null +++ b/src/components/analytics/exif/contributiongraph.tsx @@ -0,0 +1,53 @@ +import React from 'react'; + +// Function to generate the last 365 days of contribution data +const generateContributions = () => { + const contributions: number[][] = Array.from({ length: 7 }, () => Array(52).fill(0)); + for (let week = 0; week < 52; week++) { + for (let day = 0; day < 7; day++) { + // Generate a random number of contributions for each day (0 to 5 for example) + contributions[day][week] = Math.floor(Math.random() * 6); + } + } + return contributions; +}; + +// Function to determine the background color based on the contribution count +const getColor = (count: number) => { + if (count === 0) return ''; + if (count === 1) return 'bg-green-200'; + if (count === 2) return 'bg-green-400'; + if (count === 3) return 'bg-green-500'; + if (count === 4) return 'bg-green-600'; + return 'bg-green-800'; +}; + +const ContributionGraph: React.FC = () => { + const contributions = generateContributions(); + + return ( +
+

GitHub Contribution Graph - Past Year

+
+ + + {contributions.map((dayContributions, dayIndex) => ( + + {dayContributions.map((count, weekIndex) => ( + + ))} + + ))} + +
+
+
+ ); +}; + +export default ContributionGraph; diff --git a/src/components/ui/hover-card.tsx b/src/components/ui/hover-card.tsx new file mode 100644 index 0000000..863ff01 --- /dev/null +++ b/src/components/ui/hover-card.tsx @@ -0,0 +1,27 @@ +import * as React from "react" +import * as HoverCardPrimitive from "@radix-ui/react-hover-card" + +import { cn } from "@/lib/utils" + +const HoverCard = HoverCardPrimitive.Root + +const HoverCardTrigger = HoverCardPrimitive.Trigger + +const HoverCardContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( + +)) +HoverCardContent.displayName = HoverCardPrimitive.Content.displayName + +export { HoverCard, HoverCardTrigger, HoverCardContent } diff --git a/src/components/ui/linechardots.tsx b/src/components/ui/linechardots.tsx new file mode 100644 index 0000000..748b1c6 --- /dev/null +++ b/src/components/ui/linechardots.tsx @@ -0,0 +1,100 @@ +import { CartesianGrid, LabelList, Line, LineChart, XAxis, YAxis } from "recharts"; + +import { + ChartConfig, + ChartContainer, + ChartTooltip, + ChartTooltipContent, +} from "@/components/ui/chart"; +import { useMemo } from "react"; +import { CHART_COLORS } from "@/config/constants/chart.constant"; + +export interface ILineChartData { + label: string; + value: number; +} + +interface ChartProps { + data: ILineChartData[]; + topLabel?: string; + loading?: boolean; + errorMessage?: string | null; +} + +export function LineChartDots({ + data: _data, + topLabel, + loading, + errorMessage, +}: ChartProps) { + const data = useMemo( + () => + _data.map((item, index) => ({ + ...item, + fill: CHART_COLORS[index] || "#000000", + })), + [_data] + ); + + const chartConfig = useMemo(() => { + let config: ChartConfig = {}; + data.map((data) => { + config[data.label as string] = { + label: data.label, + color: "hsl(var(--chart-1))", + }; + }); + return config; + }, [data]); + + if (loading) { + return

Loading...

; + } + + if (errorMessage) { + return

{errorMessage}

; + } + + return ( + + + + {/* Added YAxis for the count */} + + + } + /> + + + chartConfig[value]?.label + } + /> + + + + ); +} diff --git a/src/components/ui/radarchartdpts.tsx b/src/components/ui/radarchartdpts.tsx new file mode 100644 index 0000000..51ddc39 --- /dev/null +++ b/src/components/ui/radarchartdpts.tsx @@ -0,0 +1,78 @@ +"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" +import { useMemo } from "react" +import { CHART_COLORS } from "@/config/constants/chart.constant" + +export const description = "A radar chart with dots" +export interface IRadarChartData { + label: string; + value: number; + color?: string; // Add color property here +} + +interface ChartProps { + data: IRadarChartData[]; + topLabel?: string; + loading?: boolean; + errorMessage?: string | null; +} + +const chartConfig = { + label: { + label: "label", + color: "#FF0000", + }, +} satisfies ChartConfig + +export default function RadarChartDots({ data: _data, topLabel, loading, errorMessage }: ChartProps) { + const chartData = _data + + return ( + + + + + + } /> + + {/* Set grid color to white */} + ( + + )} + /> + + + + + ) +} diff --git a/src/pages/analytics/exif.tsx b/src/pages/analytics/exif.tsx index fc944dd..cd25596 100644 --- a/src/pages/analytics/exif.tsx +++ b/src/pages/analytics/exif.tsx @@ -1,7 +1,18 @@ import { Inter } from "next/font/google"; import PageLayout from "@/components/layouts/PageLayout"; import Header from "@/components/shared/Header"; -import EXIFDistribution, { IEXIFDistributionProps } from "@/components/analytics/exif/EXIFDistribution"; +import EXIFDistribution, { + IEXIFDistributionProps, +} from "@/components/analytics/exif/EXIFDistribution"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import ContributionGraph from "@/components/analytics/exif/contributiongraph"; const inter = Inter({ subsets: ["latin"] }); @@ -42,36 +53,82 @@ const exifCharts: IEXIFDistributionProps[] = [ description: "Distribution of ISO", }, { - column: 'exposureTime', - title: 'Exposure Time', - description: 'Distribution of exposure time' + column: "exposureTime", + title: "Exposure Time", + description: "Distribution of exposure time", }, { - column: 'lensModel', - title: 'Lens Model', - description: 'Distribution of lens model' + column: "lensModel", + title: "Lens Model", + description: "Distribution of lens model", }, { - column: 'projectionType', - title: 'Projection Type', - description: 'Distribution of projection type' - } -] + column: "projectionType", + title: "Projection Type", + description: "Distribution of projection type", + }, +]; export default function ExifDataAnalytics() { return ( -
+
+
+ + + Total Files + + + {(() => { + const a = 700000; + return a.toLocaleString(); + })()} + + + + + Images + + + {(() => { + const a = 600000; + return a.toLocaleString(); + })()} + + + + + Videos + + + {(() => { + const a = 100000; + return a.toLocaleString(); + })()} + + + + + Live + + + {(() => { + const a = 50000; + return a.toLocaleString(); + })()} + + +
+
{exifCharts.map((chart) => ( - ))} +
);