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) => (
-
))}
+
);