Python 68.8%
TypeScript 18.6%
CSS 8.7%
JavaScript 3.3%
HTML 0.6%
1// -----------------------------------------------------------------------------2// Rent-Ka — Rental listings aggregator (Canada, outside Québec)3// Author: Simon-Pierre Boucher — contact@spboucher.ai4// components/CookieConsent.tsx: consent banner (cookies/storage)5// Honest by design: Rent-Ka uses NO third-party cookies or trackers.6// The selector covers local storage (preferences) and possible future7// anonymous audience measurement. Choice saved in localStorage,8// re-openable via the "rentka:openConsent" event (footer link).9// -----------------------------------------------------------------------------10import { useEffect, useState } from "react";11import { Link } from "react-router-dom";1213const KEY = "rentka-consent-v1";1415export interface Consent {16 essential: true; // always on (site operation)17 preferences: boolean; // remember filters and list/map view18 statistics: boolean; // anonymous audience measurement (if ever enabled)19 ts: number;20}2122export function getConsent(): Consent | null {23 try {24 const raw = localStorage.getItem(KEY);25 return raw ? (JSON.parse(raw) as Consent) : null;26 } catch {27 return null;28 }29}3031function save(preferences: boolean, statistics: boolean): Consent {32 const c: Consent = { essential: true, preferences, statistics, ts: Date.now() };33 try { localStorage.setItem(KEY, JSON.stringify(c)); } catch { /* storage blocked */ }34 return c;35}3637export default function CookieConsent() {38 const [visible, setVisible] = useState(false);39 const [custom, setCustom] = useState(false);40 const [prefs, setPrefs] = useState(true);41 const [stats, setStats] = useState(false);4243 useEffect(() => {44 if (getConsent() === null) setVisible(true);45 const open = () => { setVisible(true); setCustom(true); };46 window.addEventListener("rentka:openConsent", open);47 return () => window.removeEventListener("rentka:openConsent", open);48 }, []);4950 // the banner must never cover the floating controls (Filters button,51 // sticky CTA of the listing page): publish its height as a CSS variable52 // so they shift above it while it is displayed53 useEffect(() => {54 const root = document.documentElement;55 if (!visible) {56 root.style.setProperty("--consent-h", "0px");57 return;58 }59 const el = document.querySelector(".cookie-banner");60 const apply = () =>61 root.style.setProperty("--consent-h", `${(el?.getBoundingClientRect().height ?? 0) + 12}px`);62 apply();63 const ro = el ? new ResizeObserver(apply) : null;64 if (el && ro) ro.observe(el);65 return () => { ro?.disconnect(); root.style.setProperty("--consent-h", "0px"); };66 }, [visible, custom]);6768 if (!visible) return null;6970 const close = (p: boolean, s: boolean) => { save(p, s); setVisible(false); setCustom(false); };7172 return (73 <div className="cookie-banner" role="dialog" aria-modal="false" aria-label="Cookie preferences">74 <div className="cookie-inner">75 <div className="cookie-text">76 <b>🍪 Your cookies, your choice.</b>{" "}77 Rent-Ka uses no advertising trackers or third-party cookies. The site78 locally remembers your preferences (filters, map view) and your79 consent choice. <Link to="/privacy">Privacy policy</Link>80 </div>8182 {custom && (83 <div className="cookie-options">84 <label className="cookie-opt">85 <input type="checkbox" checked disabled />86 <span><b>Essential</b> — site operation (always on)</span>87 </label>88 <label className="cookie-opt">89 <input type="checkbox" checked={prefs} onChange={(e) => setPrefs(e.target.checked)} />90 <span><b>Preferences</b> — remember your filters and list/map view</span>91 </label>92 <label className="cookie-opt">93 <input type="checkbox" checked={stats} onChange={(e) => setStats(e.target.checked)} />94 <span><b>Statistics</b> — anonymous audience measurement, no profiling</span>95 </label>96 </div>97 )}9899 <div className="cookie-actions">100 {!custom && (101 <button className="btn btn-ghost" onClick={() => setCustom(true)}>102 Customize103 </button>104 )}105 <button className="btn btn-ghost" onClick={() => close(false, false)}>106 Essential only107 </button>108 {custom ? (109 <button className="btn btn-primary" onClick={() => close(prefs, stats)}>110 Save my choices111 </button>112 ) : (113 <button className="btn btn-primary" onClick={() => close(true, true)}>114 Accept all115 </button>116 )}117 </div>118 </div>119 </div>120 );121}122