Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions frontend/src/components/CookieConsentBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { useEffect, useState } from 'react';
import Cookies from 'js-cookie';

export function CookieConsentBanner() {
const [showBanner, setShowBanner] = useState(false);

useEffect(() => {
const consent = Cookies.get('analytics-consent');
if (!consent) {
setShowBanner(true);
}
}, []);

const acceptCookies = () => {
Cookies.set('analytics-consent', 'accepted', { expires: 365 });
setShowBanner(false);
window.location.reload();
};

const declineCookies = () => {
Cookies.set('analytics-consent', 'declined', { expires: 365 });
setShowBanner(false);
};

if (!showBanner) return null;

return (
<div className="fixed bottom-0 left-0 right-0 bg-gray-900 text-white p-4 z-50 shadow-lg border-t border-gray-800">
<div className="container mx-auto flex flex-col md:flex-row items-center justify-between gap-4">
<div className="text-sm">
<p className="font-semibold mb-1">We value your privacy</p>
<p className="text-gray-300">We use privacy-friendly analytics to understand how you use StarkEd so we can improve the platform. Your data remains anonymous.</p>
</div>
<div className="flex gap-3 flex-shrink-0">
<button
onClick={declineCookies}
className="px-4 py-2 text-sm font-medium text-gray-300 hover:text-white transition-colors"
>
Decline
</button>
<button
onClick={acceptCookies}
className="px-4 py-2 text-sm font-medium bg-blue-600 hover:bg-blue-700 rounded-md transition-colors"
>
Accept Analytics
</button>
</div>
</div>
</div>
);
}
14 changes: 13 additions & 1 deletion frontend/src/components/CredentialList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,19 @@ export function CredentialList({
}, [credentials]);

const handleVerifyCredential = async (credentialId: string) => {
await updateCredentialStatus(credentialId, 'pending');
try {
// Track credential verification
if (typeof window !== 'undefined' && (window as any).plausible) {
(window as any).plausible('Credential Verification', {
props: { credentialId }
});
}

const credential = credentials.find(c => c.id === credentialId);
await updateCredentialStatus(credentialId, 'pending');
} catch (error) {
console.error('Error verifying credential:', error);
}
};

const handleDownloadCredential = (credential: Credential) => {
Expand Down
26 changes: 16 additions & 10 deletions frontend/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,26 @@ import { useRouter } from 'next/router';
import Head from 'next/head';
import { appWithTranslation } from 'next-i18next';
import { ThemeProvider } from 'next-themes';
import PlausibleProvider from 'next-plausible';
import nextI18NextConfig from '../../next-i18next.config';
import { WalletProvider } from '../context/WalletContext';
import { ErrorBoundary } from '../components/ErrorBoundary';
import { GlobalShell } from '../components/PWA/GlobalShell';
import PWAInstallPrompt from '../components/PWAInstallPrompt';
import { CookieConsentBanner } from '../components/CookieConsentBanner';
import { Toaster } from 'react-hot-toast';
import '../styles/globals.css';

export function reportWebVitals(metric: any) {
if (typeof window !== 'undefined' && (window as any).plausible) {
(window as any).plausible('Web Vitals', {
props: {
metric: metric.name,
value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
},
});
}
}

function MyApp({ Component, pageProps }: AppProps) {
const router = useRouter();
const hasMounted = useRef(false);
Expand All @@ -37,13 +49,7 @@ function MyApp({ Component, pageProps }: AppProps) {
}, [router.asPath]);

return (
<>
<Head>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#3b82f6" />
<link rel="apple-touch-icon" href="/icons/icon-192x192.png" />
</Head>
<PlausibleProvider domain="starked-education.com" trackLocalhost={true}>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem storageKey="starked-theme">
<ErrorBoundary key={router.asPath}>
<WalletProvider>
Expand All @@ -60,7 +66,7 @@ function MyApp({ Component, pageProps }: AppProps) {
</div>
<GlobalShell />
<Component {...pageProps} />
<PWAInstallPrompt />
<CookieConsentBanner />
<Toaster
position="bottom-right"
toastOptions={{
Expand All @@ -70,7 +76,7 @@ function MyApp({ Component, pageProps }: AppProps) {
</WalletProvider>
</ErrorBoundary>
</ThemeProvider>
</>
</PlausibleProvider>
);
}

Expand Down
48 changes: 48 additions & 0 deletions frontend/src/pages/admin/analytics.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';
import Head from 'next/head';

export default function AnalyticsDashboard() {
return (
<>
<Head>
<title>Analytics Dashboard | StarkEd Admin</title>
</Head>
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 p-8">
<div className="max-w-7xl mx-auto">
<div className="mb-8">
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">Analytics Dashboard</h1>
<p className="text-gray-600 dark:text-gray-400 mt-2">
View platform usage, user engagement, and performance metrics.
</p>
</div>

<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden min-h-[800px] flex flex-col">
<div className="p-4 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 flex justify-between items-center">
<h2 className="font-semibold text-gray-800 dark:text-gray-200">Plausible Analytics</h2>
<a
href="https://plausible.io/starked-education.com"
target="_blank"
rel="noopener noreferrer"
className="text-sm text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 font-medium"
>
Open in Plausible
</a>
</div>
<div className="flex-grow w-full h-full p-0">
<iframe
plausible-embed="true"
src="https://plausible.io/share/starked-education.com?auth=YOUR_AUTH_TOKEN&theme=system"
scrolling="no"
frameBorder="0"
loading="lazy"
style={{ width: '100%', height: '100%', minHeight: '800px' }}
title="Plausible Analytics"
></iframe>
<script async src="https://plausible.io/js/embed.host.js"></script>
</div>
</div>
</div>
</div>
</>
);
}
7 changes: 7 additions & 0 deletions frontend/src/store/courseStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ export const useCourseStore = create<CourseStore>()(
set((state) => ({
enrolledCourses: [...state.enrolledCourses, enrolledCourse]
}));

// Track course enrollment
if (typeof window !== 'undefined' && (window as any).plausible) {
(window as any).plausible('Course Enrollment', {
props: { courseId: course.id, courseTitle: course.title }
});
}
}
},

Expand Down
Loading