diff --git a/frontend/app/analytics/page.tsx b/frontend/app/analytics/page.tsx
new file mode 100644
index 0000000..b1fb8e8
--- /dev/null
+++ b/frontend/app/analytics/page.tsx
@@ -0,0 +1,265 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import { Line } from "react-chartjs-2";
+import {
+ Chart as ChartJS,
+ CategoryScale,
+ LinearScale,
+ PointElement,
+ LineElement,
+ Title,
+ Tooltip,
+ Legend,
+ Filler,
+} from "chart.js";
+import { format, subDays } from "date-fns";
+
+ChartJS.register(
+ CategoryScale,
+ LinearScale,
+ PointElement,
+ LineElement,
+ Title,
+ Tooltip,
+ Legend,
+ Filler
+);
+
+interface AnalyticsData {
+ totalNotifications: number;
+ delivered: number;
+ acknowledged: number;
+ deliveryRate: number;
+ acknowledgmentRate: number;
+ trendData: {
+ dates: string[];
+ deliveryRates: number[];
+ acknowledgmentRates: number[];
+ };
+}
+
+const Skeleton = ({ className = "" }: { className?: string }) => (
+
+);
+
+const MetricCardSkeleton = () => (
+
+
+
+
+
+);
+
+const ChartSkeleton = () => (
+
+
+
+
+);
+
+export default function AnalyticsPage() {
+ const [loading, setLoading] = useState(true);
+ const [data, setData] = useState(null);
+ const [startDate, setStartDate] = useState(
+ format(subDays(new Date(), 30), "yyyy-MM-dd")
+ );
+ const [endDate, setEndDate] = useState(format(new Date(), "yyyy-MM-dd"));
+
+ const fetchAnalytics = async () => {
+ setLoading(true);
+ await new Promise((resolve) => setTimeout(resolve, 1200));
+
+ const dates: string[] = [];
+ const deliveryRates: number[] = [];
+ const acknowledgmentRates: number[] = [];
+
+ for (let i = 29; i >= 0; i--) {
+ const date = subDays(new Date(), i);
+ dates.push(format(date, "MMM d"));
+ deliveryRates.push(85 + Math.random() * 14);
+ acknowledgmentRates.push(60 + Math.random() * 25);
+ }
+
+ setData({
+ totalNotifications: 12847,
+ delivered: 12243,
+ acknowledged: 8721,
+ deliveryRate: 95.3,
+ acknowledgmentRate: 71.3,
+ trendData: {
+ dates,
+ deliveryRates,
+ acknowledgmentRates,
+ },
+ });
+
+ setLoading(false);
+ };
+
+ useEffect(() => {
+ fetchAnalytics();
+ }, [startDate, endDate]);
+
+ const chartData = {
+ labels: data?.trendData.dates || [],
+ datasets: [
+ {
+ label: "Delivery Rate",
+ data: data?.trendData.deliveryRates || [],
+ borderColor: "#4f46e5",
+ backgroundColor: "rgba(79, 70, 229, 0.1)",
+ fill: true,
+ tension: 0.4,
+ pointRadius: 3,
+ },
+ {
+ label: "Acknowledgment Rate",
+ data: data?.trendData.acknowledgmentRates || [],
+ borderColor: "#10b981",
+ backgroundColor: "rgba(16, 185, 129, 0.1)",
+ fill: true,
+ tension: 0.4,
+ pointRadius: 3,
+ },
+ ],
+ };
+
+ const chartOptions = {
+ responsive: true,
+ maintainAspectRatio: true,
+ plugins: {
+ legend: {
+ position: "top" as const,
+ labels: {
+ padding: 20,
+ usePointStyle: true,
+ },
+ },
+ },
+ scales: {
+ y: {
+ beginAtZero: true,
+ max: 100,
+ ticks: {
+ callback: (value: any) => `${value}%`,
+ },
+ },
+ },
+ };
+
+ return (
+
+
+
+
+
+ Analytics Dashboard
+
+
+ Actionable insights into your notification performance
+
+
+
+
+
+
+
+ {loading ? (
+ Array.from({ length: 4 }).map((_, i) => (
+
+ ))
+ ) : (
+ <>
+
+
+ Total Notifications
+
+
+ {data?.totalNotifications.toLocaleString()}
+
+
+12.5% from last period
+
+
+
+
+ Delivered
+
+
+ {data?.delivered.toLocaleString()}
+
+
+
+ {data?.deliveryRate}%
+
+
delivery rate
+
+
+
+
+
+ Acknowledged
+
+
+ {data?.acknowledged.toLocaleString()}
+
+
+
+ {data?.acknowledgmentRate}%
+
+
acknowledgment rate
+
+
+
+
+
+ Failed Deliveries
+
+
+ {data ? (data.totalNotifications - data.delivered).toLocaleString() : 0}
+
+
-2.3% from last period
+
+ >
+ )}
+
+
+
+ {loading ? (
+
+ ) : (
+
+
+ Performance Trends
+
+
+
+
+
+ )}
+
+
+
+ );
+}
diff --git a/frontend/app/globals.css b/frontend/app/globals.css
new file mode 100644
index 0000000..aba323d
--- /dev/null
+++ b/frontend/app/globals.css
@@ -0,0 +1,20 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+:root {
+ --background: #f8fafc;
+ --foreground: #0f172a;
+}
+
+body {
+ color: var(--foreground);
+ background: var(--background);
+ font-family: Arial, Helvetica, sans-serif;
+}
+
+@layer utilities {
+ .text-balance {
+ text-wrap: balance;
+ }
+}
diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx
new file mode 100644
index 0000000..7122a38
--- /dev/null
+++ b/frontend/app/layout.tsx
@@ -0,0 +1,21 @@
+import type { Metadata } from "next";
+import "./globals.css";
+
+export const metadata: Metadata = {
+ title: "NotifyChain - Analytics Dashboard",
+ description: "Actionable insights into notification performance",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx
new file mode 100644
index 0000000..f08f8dc
--- /dev/null
+++ b/frontend/app/page.tsx
@@ -0,0 +1,18 @@
+import Link from "next/link";
+
+export default function Home() {
+ return (
+
+
+
NotifyChain
+
Your analytics dashboard is ready!
+
+ View Analytics Dashboard
+
+
+
+ );
+}
diff --git a/frontend/next.config.js b/frontend/next.config.js
new file mode 100644
index 0000000..658404a
--- /dev/null
+++ b/frontend/next.config.js
@@ -0,0 +1,4 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {};
+
+module.exports = nextConfig;
diff --git a/frontend/package.json b/frontend/package.json
new file mode 100644
index 0000000..e925c04
--- /dev/null
+++ b/frontend/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "notify-chain-frontend",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint"
+ },
+ "dependencies": {
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "next": "14.2.14",
+ "chart.js": "^4.4.1",
+ "react-chartjs-2": "^5.2.0",
+ "date-fns": "^3.6.0"
+ },
+ "devDependencies": {
+ "typescript": "^5",
+ "@types/node": "^20",
+ "@types/react": "^18",
+ "@types/react-dom": "^18",
+ "postcss": "^8",
+ "tailwindcss": "^3.4.1",
+ "eslint": "^8",
+ "eslint-config-next": "14.2.14"
+ }
+}
diff --git a/frontend/postcss.config.mjs b/frontend/postcss.config.mjs
new file mode 100644
index 0000000..1a69fd2
--- /dev/null
+++ b/frontend/postcss.config.mjs
@@ -0,0 +1,8 @@
+/** @type {import('postcss-load-config').Config} */
+const config = {
+ plugins: {
+ tailwindcss: {},
+ },
+};
+
+export default config;
diff --git a/frontend/tailwind.config.ts b/frontend/tailwind.config.ts
new file mode 100644
index 0000000..d43da91
--- /dev/null
+++ b/frontend/tailwind.config.ts
@@ -0,0 +1,19 @@
+import type { Config } from "tailwindcss";
+
+const config: Config = {
+ content: [
+ "./pages/**/*.{js,ts,jsx,tsx,mdx}",
+ "./components/**/*.{js,ts,jsx,tsx,mdx}",
+ "./app/**/*.{js,ts,jsx,tsx,mdx}",
+ ],
+ theme: {
+ extend: {
+ colors: {
+ background: "var(--background)",
+ foreground: "var(--foreground)",
+ },
+ },
+ },
+ plugins: [],
+};
+export default config;
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
new file mode 100644
index 0000000..e7ff90f
--- /dev/null
+++ b/frontend/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}