diff --git a/src/components/conferences/conference-details.jsx b/src/components/conferences/conference-details.jsx
index a737ab6..2554a73 100644
--- a/src/components/conferences/conference-details.jsx
+++ b/src/components/conferences/conference-details.jsx
@@ -1,5 +1,6 @@
"use client";
+import { usePlausible } from 'next-plausible';
import { useState, useEffect, useCallback } from 'react';
import { useAuth } from '@/contexts/AuthContext';
import { useApi } from '@/hooks/use-api';
@@ -17,6 +18,7 @@ export default function ConferenceDetails({ conferenceId }) {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const { token, isLoggedIn } = useAuth();
+ const plausible = usePlausible();
const apiFetch = useApi();
const [isConnecting, setIsConnecting] = useState(false);
const [currentUserExtension, setCurrentUserExtension] = useState(null);
@@ -85,6 +87,7 @@ export default function ConferenceDetails({ conferenceId }) {
const handleConnectMe = async () => {
if (!token) return;
+ plausible('Conference Connect', {props: {room: conferenceId}});
setIsConnecting(true);
try {
const res = await apiFetch(`${process.env.NEXT_PUBLIC_API_URL}/conferences/${conferenceId}/connectme`, {
@@ -149,7 +152,7 @@ export default function ConferenceDetails({ conferenceId }) {
-
-
+ { plausible('Dashboard Logout'); logout(); }} className="w-full sm:w-auto">
Logout
diff --git a/src/components/dashboard/registered-devices.jsx b/src/components/dashboard/registered-devices.jsx
index 783f067..7164a58 100644
--- a/src/components/dashboard/registered-devices.jsx
+++ b/src/components/dashboard/registered-devices.jsx
@@ -1,5 +1,6 @@
"use client";
+import { usePlausible } from 'next-plausible';
import { useState, useEffect, useCallback } from 'react';
import { useAuth } from '@/contexts/AuthContext';
import { useApi } from '@/hooks/use-api';
@@ -66,6 +67,7 @@ function DeviceCard({ device }) {
export function RegisteredDevices() {
const { token } = useAuth();
+ const plausible = usePlausible();
const [devices, setDevices] = useState([]);
const [loading, setLoading] = useState(true);
const [isInitialLoad, setIsInitialLoad] = useState(true);
@@ -81,6 +83,9 @@ export function RegisteredDevices() {
if (res.ok) {
const data = await res.json();
setDevices(Array.isArray(data) ? data : []);
+ if (isInitialLoad && Array.isArray(data) && data.length > 0) {
+ plausible('Dashboard View Devices', {props: {count: data.length}});
+ }
} else {
setDevices([]);
}
diff --git a/src/components/dashboard/voicemail.jsx b/src/components/dashboard/voicemail.jsx
index fb812ee..7c82d39 100644
--- a/src/components/dashboard/voicemail.jsx
+++ b/src/components/dashboard/voicemail.jsx
@@ -1,5 +1,6 @@
"use client";
+import { usePlausible } from 'next-plausible';
import { useEffect, useState, useCallback, useMemo } from "react";
import { useAuth } from "@/contexts/AuthContext";
import { useApi } from "@/hooks/use-api";
@@ -49,6 +50,7 @@ function formatVoicemailDate(dateStr) {
export function VoicemailPanel({ extensionId: extProp }) {
const { token } = useAuth();
+ const plausible = usePlausible();
const apiFetch = useApi();
const [extensionId, setExtensionId] = useState(extProp || null);
const [loadingExt, setLoadingExt] = useState(!extProp);
@@ -127,6 +129,9 @@ export function VoicemailPanel({ extensionId: extProp }) {
const toggleExpand = async (vm) => {
const isOpening = expandedId !== vm.messageId;
setExpandedId(isOpening ? vm.messageId : null);
+ if (isOpening) {
+ plausible('Dashboard Play Voicemail');
+ }
if (isOpening && vm.hasAudio && !audioMap[vm.messageId]?.url) {
setAudioMap(m => ({ ...m, [vm.messageId]: { url: null, loading: true } }));
try {
@@ -169,6 +174,7 @@ export function VoicemailPanel({ extensionId: extProp }) {
const downloadVoicemail = async (vm) => {
if (!vm.hasAudio) return;
+ plausible('Dashboard Download Voicemail');
try {
const res = await apiFetch(`${process.env.NEXT_PUBLIC_API_URL}/extensions/${extensionId}/voicemails/${vm.messageId}/download`);
if (!res.ok) return;
diff --git a/src/components/features.jsx b/src/components/features.jsx
index 3c78394..4e0008c 100644
--- a/src/components/features.jsx
+++ b/src/components/features.jsx
@@ -1,5 +1,7 @@
"use client"
+import { useRef, useEffect } from 'react'
+import { usePlausible } from 'next-plausible'
import { Phone, Users, VoicemailIcon, Radio, MoreHorizontal, Network, Bot, LayoutDashboard } from 'lucide-react'
import Link from 'next/link'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
@@ -67,8 +69,26 @@ const colorClasses = {
}
export default function Features() {
+ const plausible = usePlausible();
+ const featuresRef = useRef(null);
+ const hasFiredRef = useRef(false);
+
+ useEffect(() => {
+ const el = featuresRef.current;
+ if (!el) return;
+ const observer = new IntersectionObserver(([entry]) => {
+ if (entry.isIntersecting && !hasFiredRef.current) {
+ hasFiredRef.current = true;
+ plausible('Scroll to Features');
+ observer.unobserve(el);
+ }
+ }, { threshold: 0.3 });
+ observer.observe(el);
+ return () => observer.disconnect();
+ }, [plausible]);
+
return (
-
+
diff --git a/src/components/header.jsx b/src/components/header.jsx
index c1b91a4..44cb530 100644
--- a/src/components/header.jsx
+++ b/src/components/header.jsx
@@ -23,19 +23,19 @@ export default function Header() {
LiteNet
@@ -119,35 +119,35 @@ export default function Header() {
setMobileMenuOpen(false)}
+ onClick={() => { plausible('Nav Click', {props: {section: 'Features'}}); setMobileMenuOpen(false); }}
>
Features
setMobileMenuOpen(false)}
+ onClick={() => { plausible('Nav Click', {props: {section: 'Updates'}}); setMobileMenuOpen(false); }}
>
Updates
setMobileMenuOpen(false)}
+ onClick={() => { plausible('Nav Click', {props: {section: 'Team'}}); setMobileMenuOpen(false); }}
>
Team
setMobileMenuOpen(false)}
+ onClick={() => { plausible('Nav Click', {props: {section: 'Hardware Survey'}}); setMobileMenuOpen(false); }}
>
Hardware Survey
setMobileMenuOpen(false)}
+ onClick={() => { plausible('Nav Click', {props: {section: 'Conferences'}}); setMobileMenuOpen(false); }}
>
Conferences
diff --git a/src/components/hero.jsx b/src/components/hero.jsx
index a8b70c4..a440d81 100644
--- a/src/components/hero.jsx
+++ b/src/components/hero.jsx
@@ -59,6 +59,7 @@ export default function Hero() {
plausible('CTA Get Started')}
asChild
>
diff --git a/src/components/team.jsx b/src/components/team.jsx
index 433aa6e..40ab378 100644
--- a/src/components/team.jsx
+++ b/src/components/team.jsx
@@ -1,5 +1,7 @@
"use client"
+import { useRef, useEffect } from 'react'
+import { usePlausible } from 'next-plausible'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
@@ -69,8 +71,26 @@ function TeamMember({ name, ext, image, discord, role }) {
}
export default function Team() {
+ const plausible = usePlausible();
+ const teamRef = useRef(null);
+ const hasFiredRef = useRef(false);
+
+ useEffect(() => {
+ const el = teamRef.current;
+ if (!el) return;
+ const observer = new IntersectionObserver(([entry]) => {
+ if (entry.isIntersecting && !hasFiredRef.current) {
+ hasFiredRef.current = true;
+ plausible('Scroll to Team');
+ observer.unobserve(el);
+ }
+ }, { threshold: 0.3 });
+ observer.observe(el);
+ return () => observer.disconnect();
+ }, [plausible]);
+
return (
-