diff --git a/src/app/client-layout.tsx b/src/app/client-layout.tsx index 365cd5e..026afe6 100644 --- a/src/app/client-layout.tsx +++ b/src/app/client-layout.tsx @@ -8,6 +8,7 @@ import { QueryClientProvider } from "@tanstack/react-query"; import { queryClient } from "@/lib/react-query/queryClient"; import { useEffect, useState } from "react"; import { usePathname } from "next/navigation"; +import { trackPageView } from "@/lib/firebase/analytics"; export default function ClientLayout({ children, @@ -22,13 +23,16 @@ export default function ClientLayout({ const isAdminRoute = pathname?.startsWith("/dashboard"); useEffect(() => { - if(isAdminRoute) { - document.body.style.backgroundColor = "#fff" - } - else { - document.body.style.backgroundColor = "#080d14" - } - },[isAdminRoute]) + if (isAdminRoute) { + document.body.style.backgroundColor = "#fff"; + } else { + document.body.style.backgroundColor = "#080d14"; + } + }, [isAdminRoute]); + + useEffect(() => { + trackPageView(pathname); + }, [pathname]); return ( diff --git a/src/app/dashboard/analytics/page.tsx b/src/app/dashboard/analytics/page.tsx index aa19429..e51d49b 100644 --- a/src/app/dashboard/analytics/page.tsx +++ b/src/app/dashboard/analytics/page.tsx @@ -1,12 +1,124 @@ "use client"; +import { useEffect, useState } from "react"; +import { + collection, + query, + orderBy, + getDocs, + where, + Timestamp, + deleteDoc, +} from "firebase/firestore"; +import { db } from "@/lib/firebase/firebase"; +import { + LineChart, + Line, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, +} from "recharts"; -import ProtectedRoute from "@/components/admin/auth/ProtectedRoute"; -import { Analytics } from "@vercel/analytics/react"; +interface PageViewData { + date: string; + views: number; +} + +// AnalyticsDashboard.tsx +interface WeeklyViewData { + weekLabel: string; + views: number; +} export default function AnalyticsDashboard() { + const [weeklyData, setWeeklyData] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const fetchWeeklyViews = async () => { + const yearAgo = new Date(); + yearAgo.setFullYear(yearAgo.getFullYear() - 1); + + const q = query( + collection(db, "weeklyViews"), + where("timestamp", ">=", yearAgo), + orderBy("timestamp", "asc"), + ); + + const snapshot = await getDocs(q); + const viewsByWeek: { [key: string]: number } = {}; + + // Get unique week-year combinations + snapshot.forEach((doc) => { + const data = doc.data(); + const key = `${data.year}-${data.weekNumber}`; + viewsByWeek[key] = (viewsByWeek[key] || 0) + 1; + }); + + // Sort by year and week + const formattedData = Object.entries(viewsByWeek) + .map(([key, views]) => { + const [year, week] = key.split("-"); + return { + weekLabel: `W${week}-${year}`, + views, + sortKey: `${year}${week.padStart(2, "0")}`, + }; + }) + .sort((a, b) => a.sortKey.localeCompare(b.sortKey)) + .map(({ weekLabel, views }) => ({ weekLabel, views })); + + setWeeklyData(formattedData); + setLoading(false); + }; + + fetchWeeklyViews(); + }, []); + + if (loading) + return ( +
Loading...
+ ); + + const totalViews = weeklyData.reduce((sum, week) => sum + week.views, 0); + const avgViews = Math.round(totalViews / weeklyData.length); + return ( - - - +
+
+
+

Total Views (in past year)

+

{totalViews}

+
+ +
+

Average Weekly Views

+

{avgViews}

+
+
+ +
+

Weekly Page Views

+
+ + + + + + + + + + +
+
+
); } diff --git a/src/lib/firebase/analytics.tsx b/src/lib/firebase/analytics.tsx new file mode 100644 index 0000000..3b4dda6 --- /dev/null +++ b/src/lib/firebase/analytics.tsx @@ -0,0 +1,38 @@ +// analytics.ts +import { db } from "@/lib/firebase/firebase"; +import { + collection, + addDoc, + getDocs, + query, + where, + serverTimestamp, +} from "firebase/firestore"; + +export const getPageViews = async (startDate: Date, endDate: Date) => { + const q = query( + collection(db, "pageViews"), + where("timestamp", ">=", startDate), + where("timestamp", "<=", endDate), + ); + return await getDocs(q); +}; + +function getWeekNumber(date: Date): number { + const firstDayOfYear = new Date(date.getFullYear(), 0, 1); + const pastDaysOfYear = (date.getTime() - firstDayOfYear.getTime()) / 86400000; + return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7); +} + +export const trackPageView = async (page: string) => { + const now = new Date(); + const weekNumber = getWeekNumber(now); + const year = now.getFullYear(); + + await addDoc(collection(db, "weeklyViews"), { + page, + weekNumber, + year, + timestamp: serverTimestamp(), + }); +}; diff --git a/src/lib/firebase/firebase.js b/src/lib/firebase/firebase.js index 545d8c7..77ebba8 100644 --- a/src/lib/firebase/firebase.js +++ b/src/lib/firebase/firebase.js @@ -16,7 +16,7 @@ const firebaseConfig = { storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, - measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID + measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID, }; // Initialize Firebase @@ -25,4 +25,4 @@ const auth = getAuth(app); const db = getFirestore(app); const storage = getStorage(app); -export { db, auth, app, storage }; \ No newline at end of file +export { db, auth, app, storage }; diff --git a/src/lib/firebase/firebaseOperations.tsx b/src/lib/firebase/firebaseOperations.tsx index ca34da7..f6cf20a 100644 --- a/src/lib/firebase/firebaseOperations.tsx +++ b/src/lib/firebase/firebaseOperations.tsx @@ -1,5 +1,12 @@ -import { db } from '@/lib/firebase/firebase'; -import { collection, getDocs, addDoc, updateDoc, deleteDoc, doc } from 'firebase/firestore'; +import { db } from "@/lib/firebase/firebase"; +import { + collection, + getDocs, + addDoc, + updateDoc, + deleteDoc, + doc, +} from "firebase/firestore"; export interface Project { id: string; //ID of the project NOT A COLUMN IN THE DATABASE @@ -21,11 +28,10 @@ export interface Project { Builders: string[]; // An array of builder names or identifiers } - // READ Operation export const getAllProjects = async () => { const querySnapshot = await getDocs(collection(db, "Projects")); - return querySnapshot.docs.map(doc => ({ + return querySnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), })) as Project[]; @@ -42,7 +48,7 @@ async function addData() { const docRef = await addDoc(collection(db, "Projects"), { name: "Claudio Sciotto", email: "claudio@example.com", - age: 22 + age: 22, }); console.log("Document written with ID: ", docRef.id); } catch (e) {