react-lgpd-consent 0.1.13 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -36,15 +36,55 @@ function readConsentCookie(name = DEFAULT_COOKIE_OPTS.name) {
36
36
  const raw = import_js_cookie.default.get(name);
37
37
  if (!raw) return null;
38
38
  try {
39
- return JSON.parse(raw);
39
+ const data = JSON.parse(raw);
40
+ if (!data.version) {
41
+ return migrateLegacyCookie(data);
42
+ }
43
+ if (data.version !== COOKIE_SCHEMA_VERSION) {
44
+ console.warn(
45
+ `[react-lgpd-consent] Cookie version mismatch: ${data.version} != ${COOKIE_SCHEMA_VERSION}`
46
+ );
47
+ return null;
48
+ }
49
+ return data;
40
50
  } catch {
41
51
  return null;
42
52
  }
43
53
  }
44
- function writeConsentCookie(state, opts) {
54
+ function migrateLegacyCookie(legacyData) {
55
+ try {
56
+ const now = (/* @__PURE__ */ new Date()).toISOString();
57
+ return {
58
+ version: COOKIE_SCHEMA_VERSION,
59
+ consented: legacyData.consented || false,
60
+ preferences: legacyData.preferences || { necessary: true },
61
+ consentDate: now,
62
+ // Não temos o original, usar data atual
63
+ lastUpdate: now,
64
+ source: "banner",
65
+ // Assumir origem banner
66
+ isModalOpen: false
67
+ // Nunca persistir estado de UI
68
+ };
69
+ } catch {
70
+ return null;
71
+ }
72
+ }
73
+ function writeConsentCookie(state, source = "banner", opts) {
45
74
  if (typeof document === "undefined") return;
75
+ const now = (/* @__PURE__ */ new Date()).toISOString();
46
76
  const o = { ...DEFAULT_COOKIE_OPTS, ...opts };
47
- import_js_cookie.default.set(o.name, JSON.stringify(state), {
77
+ const cookieData = {
78
+ version: COOKIE_SCHEMA_VERSION,
79
+ consented: state.consented,
80
+ preferences: state.preferences,
81
+ consentDate: state.consentDate || now,
82
+ // Preservar data original ou usar atual
83
+ lastUpdate: now,
84
+ source
85
+ // isModalOpen NÃO é persistido (campo de UI apenas)
86
+ };
87
+ import_js_cookie.default.set(o.name, JSON.stringify(cookieData), {
48
88
  expires: o.maxAgeDays,
49
89
  sameSite: o.sameSite,
50
90
  secure: o.secure,
@@ -56,7 +96,7 @@ function removeConsentCookie(opts) {
56
96
  const o = { ...DEFAULT_COOKIE_OPTS, ...opts };
57
97
  import_js_cookie.default.remove(o.name, { path: o.path });
58
98
  }
59
- var import_js_cookie, DEFAULT_COOKIE_OPTS;
99
+ var import_js_cookie, DEFAULT_COOKIE_OPTS, COOKIE_SCHEMA_VERSION;
60
100
  var init_cookieUtils = __esm({
61
101
  "src/utils/cookieUtils.ts"() {
62
102
  "use strict";
@@ -68,6 +108,7 @@ var init_cookieUtils = __esm({
68
108
  secure: typeof window !== "undefined" ? window.location.protocol === "https:" : false,
69
109
  path: "/"
70
110
  };
111
+ COOKIE_SCHEMA_VERSION = "1.0";
71
112
  }
72
113
  });
73
114
 
@@ -145,10 +186,81 @@ var init_theme = __esm({
145
186
  }
146
187
  });
147
188
 
189
+ // src/context/CategoriesContext.tsx
190
+ function CategoriesProvider({
191
+ categories,
192
+ children
193
+ }) {
194
+ const value = React.useMemo(() => categories || [], [categories]);
195
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CategoriesCtx.Provider, { value, children });
196
+ }
197
+ function useCustomCategories() {
198
+ return React.useContext(CategoriesCtx);
199
+ }
200
+ function useAllCategories() {
201
+ const customCategories = useCustomCategories();
202
+ return React.useMemo(() => {
203
+ const defaultCategories = [
204
+ {
205
+ id: "necessary",
206
+ name: "Cookies Necess\xE1rios",
207
+ description: "Essenciais para o funcionamento b\xE1sico do site. Incluem cookies de sess\xE3o, autentica\xE7\xE3o e seguran\xE7a.",
208
+ essential: true,
209
+ cookies: ["PHPSESSID", "JSESSIONID", "cookieConsent", "csrf_token"]
210
+ },
211
+ {
212
+ id: "analytics",
213
+ name: "Analytics e Estat\xEDsticas",
214
+ description: "Permitem medir audi\xEAncia e desempenho, gerando estat\xEDsticas an\xF4nimas de uso.",
215
+ essential: false,
216
+ cookies: ["_ga", "_ga_*", "_gid", "_gat", "gtag"]
217
+ },
218
+ {
219
+ id: "functional",
220
+ name: "Cookies Funcionais",
221
+ description: "Melhoram a experi\xEAncia do usu\xE1rio, lembrando prefer\xEAncias e configura\xE7\xF5es.",
222
+ essential: false,
223
+ cookies: ["language", "theme", "timezone", "preferences"]
224
+ },
225
+ {
226
+ id: "marketing",
227
+ name: "Marketing e Publicidade",
228
+ description: "Utilizados para publicidade direcionada e medi\xE7\xE3o de campanhas publicit\xE1rias.",
229
+ essential: false,
230
+ cookies: ["_fbp", "fr", "tr", "ads_*", "doubleclick"]
231
+ },
232
+ {
233
+ id: "social",
234
+ name: "Redes Sociais",
235
+ description: "Permitem compartilhamento e integra\xE7\xE3o com redes sociais como Facebook, YouTube, etc.",
236
+ essential: false,
237
+ cookies: ["__Secure-*", "sb", "datr", "c_user", "social_*"]
238
+ },
239
+ {
240
+ id: "personalization",
241
+ name: "Personaliza\xE7\xE3o",
242
+ description: "Adaptam o conte\xFAdo e interface \xE0s prefer\xEAncias individuais do usu\xE1rio.",
243
+ essential: false,
244
+ cookies: ["personalization_*", "content_*", "layout_*"]
245
+ }
246
+ ];
247
+ return [...defaultCategories, ...customCategories];
248
+ }, [customCategories]);
249
+ }
250
+ var React, import_jsx_runtime, CategoriesCtx;
251
+ var init_CategoriesContext = __esm({
252
+ "src/context/CategoriesContext.tsx"() {
253
+ "use strict";
254
+ React = __toESM(require("react"), 1);
255
+ import_jsx_runtime = require("react/jsx-runtime");
256
+ CategoriesCtx = React.createContext([]);
257
+ }
258
+ });
259
+
148
260
  // src/components/Branding.tsx
149
261
  function Branding({ variant, hidden = false }) {
150
262
  if (hidden) return null;
151
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
263
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
152
264
  import_Typography.default,
153
265
  {
154
266
  variant: "caption",
@@ -159,7 +271,7 @@ function Branding({ variant, hidden = false }) {
159
271
  children: [
160
272
  "fornecido por",
161
273
  " ",
162
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
274
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
163
275
  import_Link.default,
164
276
  {
165
277
  href: "https://www.ledipo.eti.br",
@@ -176,13 +288,13 @@ function Branding({ variant, hidden = false }) {
176
288
  }
177
289
  );
178
290
  }
179
- var import_Link, import_Typography, import_jsx_runtime, brandingStyles, linkStyles;
291
+ var import_Link, import_Typography, import_jsx_runtime2, brandingStyles, linkStyles;
180
292
  var init_Branding = __esm({
181
293
  "src/components/Branding.tsx"() {
182
294
  "use strict";
183
295
  import_Link = __toESM(require("@mui/material/Link"), 1);
184
296
  import_Typography = __toESM(require("@mui/material/Typography"), 1);
185
- import_jsx_runtime = require("react/jsx-runtime");
297
+ import_jsx_runtime2 = require("react/jsx-runtime");
186
298
  brandingStyles = {
187
299
  banner: {
188
300
  fontSize: "0.65rem",
@@ -235,7 +347,7 @@ function PreferencesModal({
235
347
  setTempPreferences(preferences);
236
348
  closePreferences();
237
349
  };
238
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
350
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
239
351
  import_Dialog.default,
240
352
  {
241
353
  "aria-labelledby": "cookie-pref-title",
@@ -243,14 +355,14 @@ function PreferencesModal({
243
355
  onClose: handleCancel,
244
356
  ...DialogProps2,
245
357
  children: [
246
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_DialogTitle.default, { id: "cookie-pref-title", children: texts.modalTitle }),
247
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_DialogContent.default, { dividers: true, children: [
248
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_Typography2.default, { variant: "body2", sx: { mb: 2 }, children: texts.modalIntro }),
249
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_FormGroup.default, { children: [
250
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
358
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_DialogTitle.default, { id: "cookie-pref-title", children: texts.modalTitle }),
359
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_DialogContent.default, { dividers: true, children: [
360
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_Typography2.default, { variant: "body2", sx: { mb: 2 }, children: texts.modalIntro }),
361
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_FormGroup.default, { children: [
362
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
251
363
  import_FormControlLabel.default,
252
364
  {
253
- control: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
365
+ control: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
254
366
  import_Switch.default,
255
367
  {
256
368
  checked: tempPreferences.analytics,
@@ -263,10 +375,10 @@ function PreferencesModal({
263
375
  label: "Cookies Anal\xEDticos (medem uso do site)"
264
376
  }
265
377
  ),
266
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
378
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
267
379
  import_FormControlLabel.default,
268
380
  {
269
- control: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
381
+ control: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
270
382
  import_Switch.default,
271
383
  {
272
384
  checked: tempPreferences.marketing,
@@ -279,25 +391,25 @@ function PreferencesModal({
279
391
  label: "Cookies de Marketing/Publicidade"
280
392
  }
281
393
  ),
282
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
394
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
283
395
  import_FormControlLabel.default,
284
396
  {
285
- control: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_Switch.default, { checked: true, disabled: true }),
397
+ control: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_Switch.default, { checked: true, disabled: true }),
286
398
  label: texts.necessaryAlwaysOn
287
399
  }
288
400
  )
289
401
  ] })
290
402
  ] }),
291
- !hideBranding && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Branding, { variant: "modal" }),
292
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_DialogActions.default, { children: [
293
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_Button.default, { variant: "outlined", onClick: handleCancel, children: "Cancelar" }),
294
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_Button.default, { variant: "contained", onClick: handleSave, children: texts.save })
403
+ !hideBranding && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Branding, { variant: "modal" }),
404
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_DialogActions.default, { children: [
405
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_Button.default, { variant: "outlined", onClick: handleCancel, children: "Cancelar" }),
406
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_Button.default, { variant: "contained", onClick: handleSave, children: texts.save })
295
407
  ] })
296
408
  ]
297
409
  }
298
410
  );
299
411
  }
300
- var import_Button, import_Dialog, import_DialogActions, import_DialogContent, import_DialogTitle, import_FormControlLabel, import_FormGroup, import_Switch, import_Typography2, import_react, import_jsx_runtime2;
412
+ var import_Button, import_Dialog, import_DialogActions, import_DialogContent, import_DialogTitle, import_FormControlLabel, import_FormGroup, import_Switch, import_Typography2, import_react, import_jsx_runtime3;
301
413
  var init_PreferencesModal = __esm({
302
414
  "src/components/PreferencesModal.tsx"() {
303
415
  "use strict";
@@ -313,53 +425,90 @@ var init_PreferencesModal = __esm({
313
425
  import_react = require("react");
314
426
  init_useConsent();
315
427
  init_Branding();
316
- import_jsx_runtime2 = require("react/jsx-runtime");
428
+ import_jsx_runtime3 = require("react/jsx-runtime");
317
429
  }
318
430
  });
319
431
 
320
432
  // src/context/ConsentContext.tsx
433
+ function createInitialPreferences(customCategories) {
434
+ const prefs = { ...DEFAULT_PREFERENCES };
435
+ if (customCategories) {
436
+ customCategories.forEach((category) => {
437
+ prefs[category.id] = category.essential === true;
438
+ });
439
+ }
440
+ return prefs;
441
+ }
442
+ function createFullConsentState(consented, preferences, source, isModalOpen = false, existingState) {
443
+ const now = (/* @__PURE__ */ new Date()).toISOString();
444
+ return {
445
+ version: "1.0",
446
+ consented,
447
+ preferences,
448
+ consentDate: existingState?.consentDate || now,
449
+ lastUpdate: now,
450
+ source,
451
+ isModalOpen
452
+ };
453
+ }
321
454
  function reducer(state, action) {
322
455
  switch (action.type) {
323
- case "ACCEPT_ALL":
324
- return {
325
- consented: true,
326
- preferences: { analytics: true, marketing: true },
327
- isModalOpen: false
328
- };
329
- case "REJECT_ALL":
330
- return {
331
- consented: true,
332
- preferences: { analytics: false, marketing: false },
333
- isModalOpen: false
334
- };
456
+ case "ACCEPT_ALL": {
457
+ const prefs = createInitialPreferences(action.customCategories);
458
+ Object.keys(prefs).forEach((key) => {
459
+ prefs[key] = true;
460
+ });
461
+ return createFullConsentState(true, prefs, "banner", false, state);
462
+ }
463
+ case "REJECT_ALL": {
464
+ const prefs = createInitialPreferences(action.customCategories);
465
+ if (action.customCategories) {
466
+ action.customCategories.forEach((category) => {
467
+ if (category.essential) {
468
+ prefs[category.id] = true;
469
+ }
470
+ });
471
+ }
472
+ return createFullConsentState(true, prefs, "banner", false, state);
473
+ }
335
474
  case "SET_CATEGORY":
336
475
  return {
337
476
  ...state,
338
477
  preferences: {
339
478
  ...state.preferences,
340
479
  [action.category]: action.value
341
- }
480
+ },
481
+ lastUpdate: (/* @__PURE__ */ new Date()).toISOString()
342
482
  };
343
483
  case "SET_PREFERENCES":
344
- return {
345
- ...state,
346
- consented: true,
347
- preferences: action.preferences,
348
- isModalOpen: false
349
- };
484
+ return createFullConsentState(
485
+ true,
486
+ action.preferences,
487
+ "modal",
488
+ false,
489
+ state
490
+ );
350
491
  case "OPEN_MODAL":
351
492
  return { ...state, isModalOpen: true };
352
493
  case "CLOSE_MODAL":
353
- return { ...state, isModalOpen: false, consented: true };
354
- // houve interação
355
- case "RESET":
356
- return {
357
- consented: false,
358
- preferences: { ...DEFAULT_PREFERENCES },
359
- isModalOpen: false
360
- };
494
+ return createFullConsentState(
495
+ true,
496
+ state.preferences,
497
+ "modal",
498
+ false,
499
+ state
500
+ );
501
+ case "RESET": {
502
+ return createFullConsentState(
503
+ false,
504
+ createInitialPreferences(action.customCategories),
505
+ "programmatic",
506
+ false
507
+ );
508
+ }
361
509
  case "HYDRATE":
362
- return { ...action.state };
510
+ return { ...action.state, isModalOpen: false };
511
+ // Nunca hidratar com modal aberto
363
512
  default:
364
513
  return state;
365
514
  }
@@ -368,6 +517,8 @@ function ConsentProvider({
368
517
  initialState,
369
518
  texts: textsProp,
370
519
  theme,
520
+ customCategories,
521
+ scriptIntegrations,
371
522
  PreferencesModalComponent,
372
523
  preferencesModalProps = {},
373
524
  disableAutomaticModal = false,
@@ -377,29 +528,30 @@ function ConsentProvider({
377
528
  cookie: cookieOpts,
378
529
  children
379
530
  }) {
380
- const texts = React.useMemo(
531
+ const texts = React2.useMemo(
381
532
  () => ({ ...DEFAULT_TEXTS, ...textsProp ?? {} }),
382
533
  [textsProp]
383
534
  );
384
- const cookie = React.useMemo(
535
+ const cookie = React2.useMemo(
385
536
  () => ({ ...DEFAULT_COOKIE_OPTS, ...cookieOpts ?? {} }),
386
537
  [cookieOpts]
387
538
  );
388
- const appliedTheme = React.useMemo(
539
+ const appliedTheme = React2.useMemo(
389
540
  () => theme || defaultConsentTheme,
390
541
  [theme]
391
542
  );
392
- const boot = React.useMemo(() => {
543
+ const boot = React2.useMemo(() => {
393
544
  if (initialState) return { ...initialState, isModalOpen: false };
394
- return {
395
- consented: false,
396
- preferences: { ...DEFAULT_PREFERENCES },
397
- isModalOpen: false
398
- };
399
- }, [initialState]);
400
- const [state, dispatch] = React.useReducer(reducer, boot);
401
- const [isHydrated, setIsHydrated] = React.useState(false);
402
- React.useEffect(() => {
545
+ return createFullConsentState(
546
+ false,
547
+ createInitialPreferences(customCategories),
548
+ "banner",
549
+ false
550
+ );
551
+ }, [initialState, customCategories]);
552
+ const [state, dispatch] = React2.useReducer(reducer, boot);
553
+ const [isHydrated, setIsHydrated] = React2.useState(false);
554
+ React2.useEffect(() => {
403
555
  if (!initialState) {
404
556
  const saved = readConsentCookie(cookie.name);
405
557
  if (saved?.consented) {
@@ -409,33 +561,33 @@ function ConsentProvider({
409
561
  }
410
562
  setIsHydrated(true);
411
563
  }, [cookie.name, initialState]);
412
- React.useEffect(() => {
413
- if (state.consented) writeConsentCookie(state, cookie);
564
+ React2.useEffect(() => {
565
+ if (state.consented) writeConsentCookie(state, state.source, cookie);
414
566
  }, [state, cookie]);
415
- const prevConsented = React.useRef(state.consented);
416
- React.useEffect(() => {
567
+ const prevConsented = React2.useRef(state.consented);
568
+ React2.useEffect(() => {
417
569
  if (!prevConsented.current && state.consented && onConsentGiven) {
418
570
  setTimeout(() => onConsentGiven(state), 150);
419
571
  }
420
572
  prevConsented.current = state.consented;
421
573
  }, [state, onConsentGiven]);
422
- const prevPrefs = React.useRef(state.preferences);
423
- React.useEffect(() => {
574
+ const prevPrefs = React2.useRef(state.preferences);
575
+ React2.useEffect(() => {
424
576
  if (state.consented && onPreferencesSaved && prevPrefs.current !== state.preferences) {
425
577
  setTimeout(() => onPreferencesSaved(state.preferences), 150);
426
578
  prevPrefs.current = state.preferences;
427
579
  }
428
580
  }, [state, onPreferencesSaved]);
429
- const api = React.useMemo(() => {
430
- const acceptAll = () => dispatch({ type: "ACCEPT_ALL" });
431
- const rejectAll = () => dispatch({ type: "REJECT_ALL" });
581
+ const api = React2.useMemo(() => {
582
+ const acceptAll = () => dispatch({ type: "ACCEPT_ALL", customCategories });
583
+ const rejectAll = () => dispatch({ type: "REJECT_ALL", customCategories });
432
584
  const setPreference = (category, value) => dispatch({ type: "SET_CATEGORY", category, value });
433
585
  const setPreferences = (preferences) => dispatch({ type: "SET_PREFERENCES", preferences });
434
586
  const openPreferences = () => dispatch({ type: "OPEN_MODAL" });
435
587
  const closePreferences = () => dispatch({ type: "CLOSE_MODAL" });
436
588
  const resetConsent = () => {
437
589
  removeConsentCookie(cookie);
438
- dispatch({ type: "RESET" });
590
+ dispatch({ type: "RESET", customCategories });
439
591
  };
440
592
  return {
441
593
  consented: !!state.consented,
@@ -449,50 +601,52 @@ function ConsentProvider({
449
601
  closePreferences,
450
602
  resetConsent
451
603
  };
452
- }, [state, cookie]);
453
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_styles2.ThemeProvider, { theme: appliedTheme, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StateCtx.Provider, { value: state, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ActionsCtx.Provider, { value: api, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TextsCtx.Provider, { value: texts, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(HydrationCtx.Provider, { value: isHydrated, children: [
604
+ }, [state, cookie, customCategories]);
605
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_styles2.ThemeProvider, { theme: appliedTheme, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StateCtx.Provider, { value: state, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ActionsCtx.Provider, { value: api, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TextsCtx.Provider, { value: texts, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HydrationCtx.Provider, { value: isHydrated, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(CategoriesProvider, { categories: customCategories, children: [
454
606
  children,
455
- !disableAutomaticModal && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(React.Suspense, { fallback: null, children: PreferencesModalComponent ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PreferencesModalComponent, { ...preferencesModalProps }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PreferencesModal2, { hideBranding }) })
456
- ] }) }) }) }) });
607
+ !disableAutomaticModal && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(React2.Suspense, { fallback: null, children: PreferencesModalComponent ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PreferencesModalComponent, { ...preferencesModalProps }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PreferencesModal2, { hideBranding }) })
608
+ ] }) }) }) }) }) });
457
609
  }
458
610
  function useConsentStateInternal() {
459
- const ctx = React.useContext(StateCtx);
611
+ const ctx = React2.useContext(StateCtx);
460
612
  if (!ctx)
461
613
  throw new Error("useConsentState must be used within ConsentProvider");
462
614
  return ctx;
463
615
  }
464
616
  function useConsentActionsInternal() {
465
- const ctx = React.useContext(ActionsCtx);
617
+ const ctx = React2.useContext(ActionsCtx);
466
618
  if (!ctx)
467
619
  throw new Error("useConsentActions must be used within ConsentProvider");
468
620
  return ctx;
469
621
  }
470
622
  function useConsentTextsInternal() {
471
- const ctx = React.useContext(TextsCtx);
623
+ const ctx = React2.useContext(TextsCtx);
472
624
  return ctx;
473
625
  }
474
626
  function useConsentHydrationInternal() {
475
- return React.useContext(HydrationCtx);
627
+ return React2.useContext(HydrationCtx);
476
628
  }
477
- var React, import_styles2, import_jsx_runtime3, PreferencesModal2, DEFAULT_PREFERENCES, DEFAULT_TEXTS, StateCtx, ActionsCtx, TextsCtx, HydrationCtx;
629
+ var React2, import_styles2, import_jsx_runtime4, PreferencesModal2, DEFAULT_PREFERENCES, DEFAULT_TEXTS, StateCtx, ActionsCtx, TextsCtx, HydrationCtx;
478
630
  var init_ConsentContext = __esm({
479
631
  "src/context/ConsentContext.tsx"() {
480
632
  "use strict";
481
- React = __toESM(require("react"), 1);
633
+ React2 = __toESM(require("react"), 1);
482
634
  import_styles2 = require("@mui/material/styles");
483
635
  init_cookieUtils();
484
636
  init_theme();
485
- import_jsx_runtime3 = require("react/jsx-runtime");
486
- PreferencesModal2 = React.lazy(
637
+ init_CategoriesContext();
638
+ import_jsx_runtime4 = require("react/jsx-runtime");
639
+ PreferencesModal2 = React2.lazy(
487
640
  () => Promise.resolve().then(() => (init_PreferencesModal(), PreferencesModal_exports)).then((m) => ({
488
641
  default: m.PreferencesModal
489
642
  }))
490
643
  );
491
644
  DEFAULT_PREFERENCES = {
492
- analytics: false,
493
- marketing: false
645
+ necessary: true
646
+ // Sempre ativo (essencial)
494
647
  };
495
648
  DEFAULT_TEXTS = {
649
+ // Textos básicos
496
650
  bannerMessage: "Utilizamos cookies para melhorar sua experi\xEAncia.",
497
651
  acceptAll: "Aceitar todos",
498
652
  declineAll: "Recusar",
@@ -501,12 +655,29 @@ var init_ConsentContext = __esm({
501
655
  modalTitle: "Prefer\xEAncias de Cookies",
502
656
  modalIntro: "Ajuste as categorias de cookies. Cookies necess\xE1rios s\xE3o sempre utilizados para funcionalidades b\xE1sicas.",
503
657
  save: "Salvar prefer\xEAncias",
504
- necessaryAlwaysOn: "Cookies necess\xE1rios (sempre ativos)"
658
+ necessaryAlwaysOn: "Cookies necess\xE1rios (sempre ativos)",
659
+ // Textos ANPD expandidos (opcionais)
660
+ controllerInfo: void 0,
661
+ // Exibido se definido
662
+ dataTypes: void 0,
663
+ // Exibido se definido
664
+ thirdPartySharing: void 0,
665
+ // Exibido se definido
666
+ userRights: void 0,
667
+ // Exibido se definido
668
+ contactInfo: void 0,
669
+ // Exibido se definido
670
+ retentionPeriod: void 0,
671
+ // Exibido se definido
672
+ lawfulBasis: void 0,
673
+ // Exibido se definido
674
+ transferCountries: void 0
675
+ // Exibido se definido
505
676
  };
506
- StateCtx = React.createContext(null);
507
- ActionsCtx = React.createContext(null);
508
- TextsCtx = React.createContext(DEFAULT_TEXTS);
509
- HydrationCtx = React.createContext(false);
677
+ StateCtx = React2.createContext(null);
678
+ ActionsCtx = React2.createContext(null);
679
+ TextsCtx = React2.createContext(DEFAULT_TEXTS);
680
+ HydrationCtx = React2.createContext(false);
510
681
  }
511
682
  });
512
683
 
@@ -543,16 +714,24 @@ var init_useConsent = __esm({
543
714
  // src/index.ts
544
715
  var index_exports = {};
545
716
  __export(index_exports, {
717
+ COMMON_INTEGRATIONS: () => COMMON_INTEGRATIONS,
546
718
  ConsentGate: () => ConsentGate,
547
719
  ConsentProvider: () => ConsentProvider,
720
+ ConsentScriptLoader: () => ConsentScriptLoader,
548
721
  CookieBanner: () => CookieBanner,
549
722
  FloatingPreferencesButton: () => FloatingPreferencesButton,
550
723
  PreferencesModal: () => PreferencesModal,
724
+ createGoogleAnalyticsIntegration: () => createGoogleAnalyticsIntegration,
725
+ createGoogleTagManagerIntegration: () => createGoogleTagManagerIntegration,
726
+ createUserWayIntegration: () => createUserWayIntegration,
551
727
  defaultConsentTheme: () => defaultConsentTheme,
552
728
  loadScript: () => loadScript,
729
+ useAllCategories: () => useAllCategories,
553
730
  useConsent: () => useConsent,
554
731
  useConsentHydration: () => useConsentHydration,
555
- useConsentTexts: () => useConsentTexts
732
+ useConsentScriptLoader: () => useConsentScriptLoader,
733
+ useConsentTexts: () => useConsentTexts,
734
+ useCustomCategories: () => useCustomCategories
556
735
  });
557
736
  module.exports = __toCommonJS(index_exports);
558
737
 
@@ -566,7 +745,7 @@ var import_Typography3 = __toESM(require("@mui/material/Typography"), 1);
566
745
  var import_Link2 = __toESM(require("@mui/material/Link"), 1);
567
746
  init_useConsent();
568
747
  init_Branding();
569
- var import_jsx_runtime4 = require("react/jsx-runtime");
748
+ var import_jsx_runtime5 = require("react/jsx-runtime");
570
749
  function CookieBanner({
571
750
  policyLinkUrl,
572
751
  debug,
@@ -581,17 +760,17 @@ function CookieBanner({
581
760
  const isHydrated = useConsentHydration();
582
761
  const open = debug ? true : isHydrated && !consented;
583
762
  if (!open) return null;
584
- const bannerContent = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
763
+ const bannerContent = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
585
764
  import_Paper.default,
586
765
  {
587
766
  elevation: 3,
588
767
  sx: { p: 2, maxWidth: 720, mx: "auto" },
589
768
  ...PaperProps,
590
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_Stack.default, { spacing: 1, children: [
591
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_Typography3.default, { variant: "body2", children: [
769
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_Stack.default, { spacing: 1, children: [
770
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_Typography3.default, { variant: "body2", children: [
592
771
  texts.bannerMessage,
593
772
  " ",
594
- policyLinkUrl && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
773
+ policyLinkUrl && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
595
774
  import_Link2.default,
596
775
  {
597
776
  href: policyLinkUrl,
@@ -602,26 +781,26 @@ function CookieBanner({
602
781
  }
603
782
  )
604
783
  ] }),
605
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
784
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
606
785
  import_Stack.default,
607
786
  {
608
787
  direction: { xs: "column", sm: "row" },
609
788
  spacing: 1,
610
789
  justifyContent: "flex-end",
611
790
  children: [
612
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_Button2.default, { variant: "outlined", onClick: rejectAll, children: texts.declineAll }),
613
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_Button2.default, { variant: "contained", onClick: acceptAll, children: texts.acceptAll }),
614
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_Button2.default, { variant: "text", onClick: openPreferences, children: texts.preferences })
791
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_Button2.default, { variant: "outlined", onClick: rejectAll, children: texts.declineAll }),
792
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_Button2.default, { variant: "contained", onClick: acceptAll, children: texts.acceptAll }),
793
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_Button2.default, { variant: "text", onClick: openPreferences, children: texts.preferences })
615
794
  ]
616
795
  }
617
796
  ),
618
- !hideBranding && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Branding, { variant: "banner" })
797
+ !hideBranding && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Branding, { variant: "banner" })
619
798
  ] })
620
799
  }
621
800
  );
622
801
  if (blocking) {
623
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
624
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
802
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
803
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
625
804
  import_Box.default,
626
805
  {
627
806
  sx: {
@@ -636,7 +815,7 @@ function CookieBanner({
636
815
  }
637
816
  }
638
817
  ),
639
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
818
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
640
819
  import_Box.default,
641
820
  {
642
821
  sx: {
@@ -653,7 +832,7 @@ function CookieBanner({
653
832
  )
654
833
  ] });
655
834
  }
656
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
835
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
657
836
  import_Snackbar.default,
658
837
  {
659
838
  open,
@@ -673,11 +852,11 @@ var import_Fab = __toESM(require("@mui/material/Fab"), 1);
673
852
  var import_Tooltip = __toESM(require("@mui/material/Tooltip"), 1);
674
853
  var import_styles3 = require("@mui/material/styles");
675
854
  init_useConsent();
676
- var import_jsx_runtime5 = require("react/jsx-runtime");
855
+ var import_jsx_runtime6 = require("react/jsx-runtime");
677
856
  function FloatingPreferencesButton({
678
857
  position = "bottom-right",
679
858
  offset = 24,
680
- icon = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_CookieOutlined.default, {}),
859
+ icon = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_CookieOutlined.default, {}),
681
860
  tooltip,
682
861
  FabProps,
683
862
  hideWhenConsented = false
@@ -706,7 +885,7 @@ function FloatingPreferencesButton({
706
885
  return { ...styles, bottom: offset, right: offset };
707
886
  }
708
887
  };
709
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_Tooltip.default, { title: tooltipText, placement: "top", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
888
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_Tooltip.default, { title: tooltipText, placement: "top", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
710
889
  import_Fab.default,
711
890
  {
712
891
  size: "medium",
@@ -729,14 +908,15 @@ function FloatingPreferencesButton({
729
908
  // src/index.ts
730
909
  init_ConsentContext();
731
910
  init_useConsent();
911
+ init_CategoriesContext();
732
912
 
733
913
  // src/utils/ConsentGate.tsx
734
914
  init_useConsent();
735
- var import_jsx_runtime6 = require("react/jsx-runtime");
915
+ var import_jsx_runtime7 = require("react/jsx-runtime");
736
916
  function ConsentGate(props) {
737
917
  const { preferences } = useConsent();
738
918
  if (!preferences[props.category]) return null;
739
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: props.children });
919
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: props.children });
740
920
  }
741
921
 
742
922
  // src/utils/scriptLoader.ts
@@ -778,16 +958,162 @@ function loadScript(id, src, category = null, attrs = {}) {
778
958
 
779
959
  // src/index.ts
780
960
  init_theme();
961
+
962
+ // src/utils/ConsentScriptLoader.tsx
963
+ var React3 = __toESM(require("react"), 1);
964
+ init_useConsent();
965
+ function ConsentScriptLoader({
966
+ integrations,
967
+ reloadOnChange = false
968
+ }) {
969
+ const { preferences, consented } = useConsent();
970
+ const loadedScripts = React3.useRef(/* @__PURE__ */ new Set());
971
+ React3.useEffect(() => {
972
+ if (!consented) return;
973
+ integrations.forEach(async (integration) => {
974
+ const shouldLoad = preferences[integration.category];
975
+ const alreadyLoaded = loadedScripts.current.has(integration.id);
976
+ if (shouldLoad && (!alreadyLoaded || reloadOnChange)) {
977
+ try {
978
+ await loadScript(
979
+ integration.id,
980
+ integration.src,
981
+ integration.category,
982
+ // Categoria dinâmica
983
+ integration.attrs
984
+ );
985
+ if (integration.init) {
986
+ integration.init();
987
+ }
988
+ loadedScripts.current.add(integration.id);
989
+ console.log(
990
+ `\u2705 Script loaded: ${integration.id} (${integration.category})`
991
+ );
992
+ } catch (error) {
993
+ console.error(`\u274C Failed to load script: ${integration.id}`, error);
994
+ }
995
+ }
996
+ });
997
+ }, [preferences, consented, integrations, reloadOnChange]);
998
+ return null;
999
+ }
1000
+ function useConsentScriptLoader() {
1001
+ const { preferences, consented } = useConsent();
1002
+ return React3.useCallback(
1003
+ async (integration) => {
1004
+ if (!consented) {
1005
+ console.warn(
1006
+ `\u26A0\uFE0F Cannot load script ${integration.id}: No consent given`
1007
+ );
1008
+ return false;
1009
+ }
1010
+ const shouldLoad = preferences[integration.category];
1011
+ if (!shouldLoad) {
1012
+ console.warn(
1013
+ `\u26A0\uFE0F Cannot load script ${integration.id}: Category '${integration.category}' not consented`
1014
+ );
1015
+ return false;
1016
+ }
1017
+ try {
1018
+ await loadScript(
1019
+ integration.id,
1020
+ integration.src,
1021
+ integration.category,
1022
+ // Categoria dinâmica
1023
+ integration.attrs
1024
+ );
1025
+ if (integration.init) {
1026
+ integration.init();
1027
+ }
1028
+ console.log(
1029
+ `\u2705 Script loaded: ${integration.id} (${integration.category})`
1030
+ );
1031
+ return true;
1032
+ } catch (error) {
1033
+ console.error(`\u274C Failed to load script: ${integration.id}`, error);
1034
+ return false;
1035
+ }
1036
+ },
1037
+ [preferences, consented]
1038
+ );
1039
+ }
1040
+
1041
+ // src/utils/scriptIntegrations.ts
1042
+ function createGoogleAnalyticsIntegration(config) {
1043
+ return {
1044
+ id: "google-analytics",
1045
+ category: "analytics",
1046
+ src: `https://www.googletagmanager.com/gtag/js?id=${config.measurementId}`,
1047
+ init: () => {
1048
+ if (typeof window !== "undefined") {
1049
+ let gtag2 = function(...args) {
1050
+ window.dataLayer.push(arguments);
1051
+ };
1052
+ var gtag = gtag2;
1053
+ window.dataLayer = window.dataLayer || [];
1054
+ window.gtag = gtag2;
1055
+ gtag2("js", /* @__PURE__ */ new Date());
1056
+ gtag2("config", config.measurementId, config.config || {});
1057
+ }
1058
+ },
1059
+ attrs: { async: "true" }
1060
+ };
1061
+ }
1062
+ function createGoogleTagManagerIntegration(config) {
1063
+ return {
1064
+ id: "google-tag-manager",
1065
+ category: "analytics",
1066
+ src: `https://www.googletagmanager.com/gtm.js?id=${config.containerId}`,
1067
+ init: () => {
1068
+ if (typeof window !== "undefined") {
1069
+ const dataLayerName = config.dataLayerName || "dataLayer";
1070
+ window[dataLayerName] = window[dataLayerName] || [];
1071
+ window[dataLayerName].push({
1072
+ "gtm.start": (/* @__PURE__ */ new Date()).getTime(),
1073
+ event: "gtm.js"
1074
+ });
1075
+ }
1076
+ }
1077
+ };
1078
+ }
1079
+ function createUserWayIntegration(config) {
1080
+ return {
1081
+ id: "userway",
1082
+ category: "marketing",
1083
+ // ou poderia ser uma categoria 'accessibility'
1084
+ src: `https://cdn.userway.org/widget.js`,
1085
+ init: () => {
1086
+ if (typeof window !== "undefined") {
1087
+ window.UserWayWidgetApp = window.UserWayWidgetApp || {};
1088
+ window.UserWayWidgetApp.accountId = config.accountId;
1089
+ }
1090
+ },
1091
+ attrs: { "data-account": config.accountId }
1092
+ };
1093
+ }
1094
+ var COMMON_INTEGRATIONS = {
1095
+ googleAnalytics: createGoogleAnalyticsIntegration,
1096
+ googleTagManager: createGoogleTagManagerIntegration,
1097
+ userway: createUserWayIntegration
1098
+ };
781
1099
  // Annotate the CommonJS export names for ESM import in node:
782
1100
  0 && (module.exports = {
1101
+ COMMON_INTEGRATIONS,
783
1102
  ConsentGate,
784
1103
  ConsentProvider,
1104
+ ConsentScriptLoader,
785
1105
  CookieBanner,
786
1106
  FloatingPreferencesButton,
787
1107
  PreferencesModal,
1108
+ createGoogleAnalyticsIntegration,
1109
+ createGoogleTagManagerIntegration,
1110
+ createUserWayIntegration,
788
1111
  defaultConsentTheme,
789
1112
  loadScript,
1113
+ useAllCategories,
790
1114
  useConsent,
791
1115
  useConsentHydration,
792
- useConsentTexts
1116
+ useConsentScriptLoader,
1117
+ useConsentTexts,
1118
+ useCustomCategories
793
1119
  });