Files
ntspi-app/resources/js/services/analytics.js
T
F4ilji d6c0f48d48 feat(analytics): add site analytics with grouped navigation sections
- Add Analytics container: models, tasks, jobs, controllers, routes, services
- Add analytics config with section groups (structure, institute, content, services)
- Add database tables: analytics_sessions, analytics_hits, analytics_daily_stats
- Add frontend: Analytics page, section detail, chart, overview, navigation components
- Add consent banner and JS tracking service (sendBeacon + Inertia listener)
- Add permission sync for view_analytics, view_any_analytic
- Fix trailing slash URL matching in GetNavigationSectionsTask
- Fix event propagation on expand arrows in navigation sections
- Fix RecordVisitJob to not depend on dropped analytics_geo_cache table
- Replace plain text views/visitors with badge-style icons
- Group navigation sections by category with separate blocks
- Add config-driven non-CMS sections (faculties, divisions, DPO, journals)
- Remove Events, TV, Sveden from analytics sections
2026-09-18 22:18:04 +05:00

69 lines
1.7 KiB
JavaScript

import { router } from '@inertiajs/vue3'
const CONSENT_KEY = 'analytics_consent'
const VISITOR_KEY = '_vid'
function getVisitorId() {
let vid = localStorage.getItem(VISITOR_KEY)
if (!vid) {
vid = crypto.randomUUID()
localStorage.setItem(VISITOR_KEY, vid)
}
return vid
}
function isExcludedPath() {
const path = window.location.pathname
return path.startsWith('/dashboard') || path.startsWith('/admin')
}
function sendPayload(payload) {
const blob = new Blob([JSON.stringify(payload)], { type: 'application/json' })
if (navigator.sendBeacon) {
const sent = navigator.sendBeacon('/api/track/hit', blob)
if (sent) return
}
fetch('/api/track/hit', {
method: 'POST',
body: JSON.stringify(payload),
headers: { 'Content-Type': 'application/json' },
keepalive: true,
}).catch(() => {})
}
export function getConsent() {
return localStorage.getItem(CONSENT_KEY)
}
export function trackCurrentPage() {
if (typeof window === 'undefined') return
if (getConsent() !== 'granted') return
if (isExcludedPath()) return
sendPayload({
visitor_id: getVisitorId(),
url: window.location.pathname + window.location.search,
referrer: document.referrer || null,
screen_resolution: `${window.screen.width}x${window.screen.height}`,
title: document.title,
timestamp: Date.now(),
})
}
let listenerRegistered = false
export function initAnalytics() {
if (typeof window === 'undefined') return
if (!listenerRegistered) {
listenerRegistered = true
router.on('navigate', () => {
setTimeout(trackCurrentPage, 100)
})
}
window._ntspiTrackHit = trackCurrentPage
}