Files
GNX-WEB/frontEnd/components/shared/layout/CookieConsentContext.tsx
Iliyan Angelov 366f28677a update
2025-11-24 03:52:08 +02:00

414 lines
11 KiB
TypeScript

'use client';
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
// Types for cookie consent
export interface CookiePreferences {
necessary: boolean;
functional: boolean;
}
export interface ConsentAuditLog {
timestamp: string;
action: 'consent_given' | 'consent_updated' | 'consent_withdrawn' | 'settings_opened';
preferences: CookiePreferences;
userAgent: string;
ipAddress?: string;
sessionId: string;
}
export interface CookieConsentConfig {
version: string;
companyName: string;
privacyPolicyUrl: string;
cookiePolicyUrl: string;
dataControllerEmail: string;
retentionPeriod: number; // days
enableAuditLog: boolean;
enableDetailedSettings: boolean;
showPrivacyNotice: boolean;
}
export interface CookieConsentState {
hasConsented: boolean;
preferences: CookiePreferences;
showBanner: boolean;
showSettings: boolean;
consentDate?: string;
lastUpdated?: string;
auditLog: ConsentAuditLog[];
}
export interface CookieConsentContextType {
state: CookieConsentState;
config: CookieConsentConfig;
acceptAll: () => void;
acceptNecessary: () => void;
acceptSelected: (preferences: Partial<CookiePreferences>) => void;
showSettings: () => void;
hideSettings: () => void;
updatePreferences: (preferences: Partial<CookiePreferences>) => void;
resetConsent: () => void;
withdrawConsent: () => void;
exportConsentData: () => string;
getConsentSummary: () => any;
}
// Default cookie preferences
const defaultPreferences: CookiePreferences = {
necessary: true, // Always true, cannot be disabled
functional: false,
};
// Enterprise configuration
const defaultConfig: CookieConsentConfig = {
version: '2.0',
companyName: 'Your Company Name',
privacyPolicyUrl: '/policy?type=privacy',
cookiePolicyUrl: '/policy?type=privacy',
dataControllerEmail: 'privacy@yourcompany.com',
retentionPeriod: 365, // 1 year
enableAuditLog: true,
enableDetailedSettings: true,
showPrivacyNotice: true,
};
// Default state
const defaultState: CookieConsentState = {
hasConsented: false,
preferences: defaultPreferences,
showBanner: false,
showSettings: false,
auditLog: [],
};
// Create context
const CookieConsentContext = createContext<CookieConsentContextType | undefined>(undefined);
// Storage keys
const CONSENT_STORAGE_KEY = 'cookie-consent-preferences';
const CONSENT_VERSION = '2.0';
// Utility functions
const generateSessionId = (): string => {
return Math.random().toString(36).substring(2) + Date.now().toString(36);
};
const createAuditLogEntry = (
action: ConsentAuditLog['action'],
preferences: CookiePreferences
): ConsentAuditLog => {
return {
timestamp: new Date().toISOString(),
action,
preferences,
userAgent: typeof window !== 'undefined' ? window.navigator.userAgent : '',
sessionId: generateSessionId(),
};
};
// Provider component
export const CookieConsentProvider: React.FC<{
children: ReactNode;
config?: Partial<CookieConsentConfig>;
}> = ({ children, config: customConfig }) => {
const [state, setState] = useState<CookieConsentState>(defaultState);
const config = { ...defaultConfig, ...customConfig };
// Load saved preferences on mount
useEffect(() => {
const loadSavedPreferences = () => {
try {
const saved = localStorage.getItem(CONSENT_STORAGE_KEY);
if (saved) {
const parsed = JSON.parse(saved);
// Check if saved version matches current version
if (parsed.version === CONSENT_VERSION) {
setState({
hasConsented: true,
preferences: parsed.preferences,
showBanner: false,
showSettings: false,
consentDate: parsed.consentDate,
lastUpdated: parsed.lastUpdated,
auditLog: parsed.auditLog || [],
});
return;
}
}
} catch (error) {
}
// Show banner if no valid consent found
setState(prev => ({ ...prev, showBanner: true }));
};
loadSavedPreferences();
}, []);
// Save preferences to localStorage with audit logging
const savePreferences = (preferences: CookiePreferences, action: ConsentAuditLog['action'] = 'consent_given') => {
try {
const auditEntry = config.enableAuditLog ? createAuditLogEntry(action, preferences) : null;
const data = {
version: CONSENT_VERSION,
preferences,
timestamp: new Date().toISOString(),
consentDate: state.consentDate || new Date().toISOString(),
lastUpdated: new Date().toISOString(),
auditLog: auditEntry ? [...state.auditLog, auditEntry] : state.auditLog,
};
localStorage.setItem(CONSENT_STORAGE_KEY, JSON.stringify(data));
// Update state with audit log
if (auditEntry) {
setState(prev => ({
...prev,
auditLog: [...prev.auditLog, auditEntry],
}));
}
} catch (error) {
}
};
// Accept all cookies
const acceptAll = () => {
const allAccepted: CookiePreferences = {
necessary: true,
functional: true,
};
setState(prev => ({
...prev,
hasConsented: true,
preferences: allAccepted,
showBanner: false,
showSettings: false,
consentDate: prev.consentDate || new Date().toISOString(),
lastUpdated: new Date().toISOString(),
}));
savePreferences(allAccepted, 'consent_given');
};
// Accept only necessary cookies
const acceptNecessary = () => {
const necessaryOnly: CookiePreferences = {
necessary: true,
functional: false,
};
setState(prev => ({
...prev,
hasConsented: true,
preferences: necessaryOnly,
showBanner: false,
showSettings: false,
consentDate: prev.consentDate || new Date().toISOString(),
lastUpdated: new Date().toISOString(),
}));
savePreferences(necessaryOnly, 'consent_given');
};
// Accept selected cookies
const acceptSelected = (preferences: Partial<CookiePreferences>) => {
const newPreferences: CookiePreferences = {
necessary: true, // Always true
functional: preferences.functional ?? false,
};
setState(prev => ({
...prev,
hasConsented: true,
preferences: newPreferences,
showBanner: false,
showSettings: false,
consentDate: prev.consentDate || new Date().toISOString(),
lastUpdated: new Date().toISOString(),
}));
savePreferences(newPreferences, 'consent_updated');
};
// Show settings modal
const showSettings = () => {
setState(prev => ({ ...prev, showSettings: true }));
// Log settings opened
if (config.enableAuditLog) {
const auditEntry = createAuditLogEntry('settings_opened', state.preferences);
setState(prev => ({
...prev,
auditLog: [...prev.auditLog, auditEntry],
}));
}
};
// Hide settings modal
const hideSettings = () => {
setState(prev => ({ ...prev, showSettings: false }));
};
// Update preferences (for settings modal)
const updatePreferences = (preferences: Partial<CookiePreferences>) => {
setState(prev => ({
...prev,
preferences: {
...prev.preferences,
...preferences,
necessary: true, // Always keep necessary as true
},
}));
};
// Reset consent (for testing or user request)
const resetConsent = () => {
try {
localStorage.removeItem(CONSENT_STORAGE_KEY);
} catch (error) {
}
setState({
hasConsented: false,
preferences: defaultPreferences,
showBanner: true,
showSettings: false,
auditLog: [],
});
};
// Withdraw consent (GDPR compliance)
const withdrawConsent = () => {
try {
localStorage.removeItem(CONSENT_STORAGE_KEY);
} catch (error) {
}
const auditEntry = config.enableAuditLog ? createAuditLogEntry('consent_withdrawn', defaultPreferences) : null;
setState({
hasConsented: false,
preferences: defaultPreferences,
showBanner: true,
showSettings: false,
auditLog: auditEntry ? [...state.auditLog, auditEntry] : state.auditLog,
});
};
// Export consent data (GDPR compliance)
const exportConsentData = (): string => {
const exportData = {
consentData: {
hasConsented: state.hasConsented,
preferences: state.preferences,
consentDate: state.consentDate,
lastUpdated: state.lastUpdated,
auditLog: state.auditLog,
},
config: {
version: config.version,
companyName: config.companyName,
retentionPeriod: config.retentionPeriod,
},
exportDate: new Date().toISOString(),
};
return JSON.stringify(exportData, null, 2);
};
// Get consent summary
const getConsentSummary = () => {
return {
hasConsented: state.hasConsented,
preferences: state.preferences,
consentDate: state.consentDate,
lastUpdated: state.lastUpdated,
auditLogCount: state.auditLog.length,
config: {
version: config.version,
companyName: config.companyName,
retentionPeriod: config.retentionPeriod,
},
};
};
const contextValue: CookieConsentContextType = {
state,
config,
acceptAll,
acceptNecessary,
acceptSelected,
showSettings,
hideSettings,
updatePreferences,
resetConsent,
withdrawConsent,
exportConsentData,
getConsentSummary,
};
return (
<CookieConsentContext.Provider value={contextValue}>
{children}
</CookieConsentContext.Provider>
);
};
// Hook to use cookie consent context
export const useCookieConsent = (): CookieConsentContextType => {
const context = useContext(CookieConsentContext);
if (context === undefined) {
throw new Error('useCookieConsent must be used within a CookieConsentProvider');
}
return context;
};
// Hook to check if specific cookie type is allowed
export const useCookiePermission = (type: keyof CookiePreferences): boolean => {
const { state } = useCookieConsent();
return state.preferences[type];
};
// Hook for functional features (only runs if functional cookies are allowed)
export const useFunctional = () => {
const { state } = useCookieConsent();
const isEnabled = state.preferences.functional && state.hasConsented;
const saveUserPreference = (key: string, value: any) => {
if (isEnabled && typeof window !== 'undefined') {
try {
localStorage.setItem(`user-preference-${key}`, JSON.stringify(value));
} catch (error) {
}
}
};
const loadUserPreference = (key: string, defaultValue?: any) => {
if (isEnabled && typeof window !== 'undefined') {
try {
const saved = localStorage.getItem(`user-preference-${key}`);
return saved ? JSON.parse(saved) : defaultValue;
} catch (error) {
return defaultValue;
}
}
return defaultValue;
};
const rememberUserAction = (action: string, data?: any) => {
if (isEnabled) {
// Implement your user action tracking logic here
}
};
return {
saveUserPreference,
loadUserPreference,
rememberUserAction,
isEnabled,
};
};