react-lgpd-consent 0.2.0 → 0.2.2
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/COMPLIANCE.md +231 -87
- package/README.md +181 -19
- package/dist/{PreferencesModal-QQOOLRDY.js → PreferencesModal-UL552BFP.js} +1 -1
- package/dist/{chunk-JTPCDTOQ.js → chunk-JAX63PBG.js} +366 -161
- package/dist/index.cjs +374 -157
- package/dist/index.d.cts +110 -19
- package/dist/index.d.ts +110 -19
- package/dist/index.js +9 -3
- package/package.json +1 -1
|
@@ -8,10 +8,208 @@ import FormControlLabel from "@mui/material/FormControlLabel";
|
|
|
8
8
|
import FormGroup from "@mui/material/FormGroup";
|
|
9
9
|
import Switch from "@mui/material/Switch";
|
|
10
10
|
import Typography2 from "@mui/material/Typography";
|
|
11
|
-
import { useEffect as
|
|
11
|
+
import { useEffect as useEffect4, useState as useState2 } from "react";
|
|
12
12
|
|
|
13
|
-
// src/context/
|
|
13
|
+
// src/context/CategoriesContext.tsx
|
|
14
14
|
import * as React2 from "react";
|
|
15
|
+
|
|
16
|
+
// src/utils/developerGuidance.ts
|
|
17
|
+
import * as React from "react";
|
|
18
|
+
var DEFAULT_PROJECT_CATEGORIES = {
|
|
19
|
+
enabledCategories: ["analytics"],
|
|
20
|
+
// Só analytics além de necessary
|
|
21
|
+
customCategories: []
|
|
22
|
+
};
|
|
23
|
+
function analyzeDeveloperConfiguration(config) {
|
|
24
|
+
const guidance = {
|
|
25
|
+
warnings: [],
|
|
26
|
+
suggestions: [],
|
|
27
|
+
activeCategoriesInfo: [],
|
|
28
|
+
usingDefaults: !config
|
|
29
|
+
};
|
|
30
|
+
const finalConfig = config || DEFAULT_PROJECT_CATEGORIES;
|
|
31
|
+
if (!config) {
|
|
32
|
+
guidance.warnings.push(
|
|
33
|
+
'LGPD-CONSENT: Nenhuma configura\xE7\xE3o de categorias especificada. Usando padr\xE3o: necessary + analytics. Para produ\xE7\xE3o, recomenda-se especificar explicitamente as categorias via prop "categories".'
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
guidance.activeCategoriesInfo.push({
|
|
37
|
+
id: "necessary",
|
|
38
|
+
name: "Cookies Necess\xE1rios",
|
|
39
|
+
description: "Essenciais para funcionamento b\xE1sico do site",
|
|
40
|
+
essential: true,
|
|
41
|
+
uiRequired: false
|
|
42
|
+
// Não precisa de toggle (sempre ativo)
|
|
43
|
+
});
|
|
44
|
+
const enabledCategories = finalConfig.enabledCategories || [];
|
|
45
|
+
const categoryNames = {
|
|
46
|
+
analytics: {
|
|
47
|
+
name: "Cookies Anal\xEDticos",
|
|
48
|
+
description: "Medem uso e performance do site"
|
|
49
|
+
},
|
|
50
|
+
functional: {
|
|
51
|
+
name: "Cookies Funcionais",
|
|
52
|
+
description: "Melhoram experi\xEAncia e funcionalidades"
|
|
53
|
+
},
|
|
54
|
+
marketing: {
|
|
55
|
+
name: "Cookies de Marketing",
|
|
56
|
+
description: "Publicidade direcionada e campanhas"
|
|
57
|
+
},
|
|
58
|
+
social: {
|
|
59
|
+
name: "Cookies de Redes Sociais",
|
|
60
|
+
description: "Integra\xE7\xE3o com plataformas sociais"
|
|
61
|
+
},
|
|
62
|
+
personalization: {
|
|
63
|
+
name: "Cookies de Personaliza\xE7\xE3o",
|
|
64
|
+
description: "Adaptam conte\xFAdo \xE0s prefer\xEAncias do usu\xE1rio"
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
enabledCategories.forEach((categoryId) => {
|
|
68
|
+
const categoryInfo = categoryNames[categoryId];
|
|
69
|
+
if (categoryInfo) {
|
|
70
|
+
guidance.activeCategoriesInfo.push({
|
|
71
|
+
id: categoryId,
|
|
72
|
+
name: categoryInfo.name,
|
|
73
|
+
description: categoryInfo.description,
|
|
74
|
+
essential: false,
|
|
75
|
+
uiRequired: true
|
|
76
|
+
// Precisa de toggle na UI
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
const customCategories = finalConfig.customCategories || [];
|
|
81
|
+
customCategories.forEach((category) => {
|
|
82
|
+
guidance.activeCategoriesInfo.push({
|
|
83
|
+
id: category.id,
|
|
84
|
+
name: category.name,
|
|
85
|
+
description: category.description,
|
|
86
|
+
essential: category.essential === true,
|
|
87
|
+
uiRequired: category.essential !== true
|
|
88
|
+
// Apenas não-essenciais precisam toggle
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
const totalToggleable = guidance.activeCategoriesInfo.filter(
|
|
92
|
+
(c) => c.uiRequired
|
|
93
|
+
).length;
|
|
94
|
+
if (totalToggleable === 0) {
|
|
95
|
+
guidance.suggestions.push(
|
|
96
|
+
'Apenas cookies necess\xE1rios est\xE3o configurados. Para compliance completo LGPD, considere adicionar categorias como "analytics" ou "functional" conforme uso real.'
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
if (totalToggleable > 5) {
|
|
100
|
+
guidance.warnings.push(
|
|
101
|
+
`${totalToggleable} categorias opcionais detectadas. UI com muitas op\xE7\xF5es pode ' +
|
|
102
|
+
'prejudicar experi\xEAncia do usu\xE1rio. Considere agrupar categorias similares.`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
const poorDescriptions = customCategories.filter(
|
|
106
|
+
(c) => !c.description || c.description.length < 10
|
|
107
|
+
);
|
|
108
|
+
if (poorDescriptions.length > 0) {
|
|
109
|
+
guidance.warnings.push(
|
|
110
|
+
`Categorias customizadas com descri\xE7\xF5es inadequadas: ${poorDescriptions.map((c) => c.id).join(", ")}. Descri\xE7\xF5es claras s\xE3o obrigat\xF3rias para compliance LGPD.`
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
return guidance;
|
|
114
|
+
}
|
|
115
|
+
function logDeveloperGuidance(guidance) {
|
|
116
|
+
const isProduction = typeof globalThis !== "undefined" && globalThis.__LGPD_PRODUCTION__ || typeof window !== "undefined" && !window.__LGPD_DEV__;
|
|
117
|
+
if (isProduction) return;
|
|
118
|
+
if (guidance.warnings.length > 0) {
|
|
119
|
+
console.group("\u{1F7E8} LGPD-CONSENT: Avisos de Configura\xE7\xE3o");
|
|
120
|
+
guidance.warnings.forEach((warning) => console.warn(warning));
|
|
121
|
+
console.groupEnd();
|
|
122
|
+
}
|
|
123
|
+
if (guidance.suggestions.length > 0) {
|
|
124
|
+
console.group("\u{1F4A1} LGPD-CONSENT: Sugest\xF5es");
|
|
125
|
+
guidance.suggestions.forEach((suggestion) => console.info(suggestion));
|
|
126
|
+
console.groupEnd();
|
|
127
|
+
}
|
|
128
|
+
if (guidance.usingDefaults) {
|
|
129
|
+
console.info(
|
|
130
|
+
'\u{1F4CB} LGPD-CONSENT: Usando configura\xE7\xE3o padr\xE3o. Para personalizar, use a prop "categories" no ConsentProvider.'
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
console.group("\u{1F527} LGPD-CONSENT: Categorias Ativas (para UI customizada)");
|
|
134
|
+
console.table(
|
|
135
|
+
guidance.activeCategoriesInfo.map((cat) => ({
|
|
136
|
+
ID: cat.id,
|
|
137
|
+
Nome: cat.name,
|
|
138
|
+
"Toggle UI?": cat.uiRequired ? "\u2705 SIM" : "\u274C N\xC3O (sempre ativo)",
|
|
139
|
+
"Essencial?": cat.essential ? "\u{1F512} SIM" : "\u2699\uFE0F N\xC3O"
|
|
140
|
+
}))
|
|
141
|
+
);
|
|
142
|
+
console.groupEnd();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/context/CategoriesContext.tsx
|
|
146
|
+
import { jsx } from "react/jsx-runtime";
|
|
147
|
+
var CategoriesContext = React2.createContext(
|
|
148
|
+
null
|
|
149
|
+
);
|
|
150
|
+
var CategoriesCtx = React2.createContext([]);
|
|
151
|
+
function CategoriesProvider({
|
|
152
|
+
children,
|
|
153
|
+
categories,
|
|
154
|
+
// LEGACY: prop antiga (apenas customCategories)
|
|
155
|
+
config
|
|
156
|
+
// NOVO: configuração completa
|
|
157
|
+
}) {
|
|
158
|
+
const contextValue = React2.useMemo(() => {
|
|
159
|
+
let finalConfig;
|
|
160
|
+
if (categories && !config) {
|
|
161
|
+
finalConfig = {
|
|
162
|
+
enabledCategories: DEFAULT_PROJECT_CATEGORIES.enabledCategories,
|
|
163
|
+
customCategories: categories
|
|
164
|
+
};
|
|
165
|
+
} else {
|
|
166
|
+
finalConfig = config || DEFAULT_PROJECT_CATEGORIES;
|
|
167
|
+
}
|
|
168
|
+
const guidance = analyzeDeveloperConfiguration(
|
|
169
|
+
config || (categories ? { customCategories: categories } : void 0)
|
|
170
|
+
);
|
|
171
|
+
const toggleableCategories = guidance.activeCategoriesInfo.filter(
|
|
172
|
+
(cat) => cat.uiRequired
|
|
173
|
+
);
|
|
174
|
+
return {
|
|
175
|
+
config: finalConfig,
|
|
176
|
+
guidance,
|
|
177
|
+
toggleableCategories,
|
|
178
|
+
allCategories: guidance.activeCategoriesInfo,
|
|
179
|
+
legacyCategories: categories || []
|
|
180
|
+
};
|
|
181
|
+
}, [config, categories]);
|
|
182
|
+
React2.useEffect(() => {
|
|
183
|
+
logDeveloperGuidance(contextValue.guidance);
|
|
184
|
+
}, [contextValue.guidance]);
|
|
185
|
+
return /* @__PURE__ */ jsx(CategoriesContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(CategoriesCtx.Provider, { value: contextValue.legacyCategories, children }) });
|
|
186
|
+
}
|
|
187
|
+
function useCategories() {
|
|
188
|
+
const context = React2.useContext(CategoriesContext);
|
|
189
|
+
if (!context) {
|
|
190
|
+
throw new Error(
|
|
191
|
+
"useCategories deve ser usado dentro de CategoriesProvider. Certifique-se de que o ConsentProvider est\xE1 envolvendo seu componente."
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
return context;
|
|
195
|
+
}
|
|
196
|
+
function useCategoryStatus(categoryId) {
|
|
197
|
+
const { allCategories } = useCategories();
|
|
198
|
+
const category = allCategories.find((cat) => cat.id === categoryId);
|
|
199
|
+
return {
|
|
200
|
+
isActive: !!category,
|
|
201
|
+
isEssential: category?.essential || false,
|
|
202
|
+
needsToggle: category?.uiRequired || false,
|
|
203
|
+
name: category?.name,
|
|
204
|
+
description: category?.description
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function useCustomCategories() {
|
|
208
|
+
return React2.useContext(CategoriesCtx);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/context/ConsentContext.tsx
|
|
212
|
+
import * as React3 from "react";
|
|
15
213
|
import { ThemeProvider } from "@mui/material/styles";
|
|
16
214
|
|
|
17
215
|
// src/utils/cookieUtils.ts
|
|
@@ -23,20 +221,61 @@ var DEFAULT_COOKIE_OPTS = {
|
|
|
23
221
|
secure: typeof window !== "undefined" ? window.location.protocol === "https:" : false,
|
|
24
222
|
path: "/"
|
|
25
223
|
};
|
|
224
|
+
var COOKIE_SCHEMA_VERSION = "1.0";
|
|
26
225
|
function readConsentCookie(name = DEFAULT_COOKIE_OPTS.name) {
|
|
27
226
|
if (typeof document === "undefined") return null;
|
|
28
227
|
const raw = Cookies.get(name);
|
|
29
228
|
if (!raw) return null;
|
|
30
229
|
try {
|
|
31
|
-
|
|
230
|
+
const data = JSON.parse(raw);
|
|
231
|
+
if (!data.version) {
|
|
232
|
+
return migrateLegacyCookie(data);
|
|
233
|
+
}
|
|
234
|
+
if (data.version !== COOKIE_SCHEMA_VERSION) {
|
|
235
|
+
console.warn(
|
|
236
|
+
`[react-lgpd-consent] Cookie version mismatch: ${data.version} != ${COOKIE_SCHEMA_VERSION}`
|
|
237
|
+
);
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
return data;
|
|
241
|
+
} catch {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function migrateLegacyCookie(legacyData) {
|
|
246
|
+
try {
|
|
247
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
248
|
+
return {
|
|
249
|
+
version: COOKIE_SCHEMA_VERSION,
|
|
250
|
+
consented: legacyData.consented || false,
|
|
251
|
+
preferences: legacyData.preferences || { necessary: true },
|
|
252
|
+
consentDate: now,
|
|
253
|
+
// Não temos o original, usar data atual
|
|
254
|
+
lastUpdate: now,
|
|
255
|
+
source: "banner",
|
|
256
|
+
// Assumir origem banner
|
|
257
|
+
isModalOpen: false
|
|
258
|
+
// Nunca persistir estado de UI
|
|
259
|
+
};
|
|
32
260
|
} catch {
|
|
33
261
|
return null;
|
|
34
262
|
}
|
|
35
263
|
}
|
|
36
|
-
function writeConsentCookie(state, opts) {
|
|
264
|
+
function writeConsentCookie(state, source = "banner", opts) {
|
|
37
265
|
if (typeof document === "undefined") return;
|
|
266
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
38
267
|
const o = { ...DEFAULT_COOKIE_OPTS, ...opts };
|
|
39
|
-
|
|
268
|
+
const cookieData = {
|
|
269
|
+
version: COOKIE_SCHEMA_VERSION,
|
|
270
|
+
consented: state.consented,
|
|
271
|
+
preferences: state.preferences,
|
|
272
|
+
consentDate: state.consentDate || now,
|
|
273
|
+
// Preservar data original ou usar atual
|
|
274
|
+
lastUpdate: now,
|
|
275
|
+
source
|
|
276
|
+
// isModalOpen NÃO é persistido (campo de UI apenas)
|
|
277
|
+
};
|
|
278
|
+
Cookies.set(o.name, JSON.stringify(cookieData), {
|
|
40
279
|
expires: o.maxAgeDays,
|
|
41
280
|
sameSite: o.sameSite,
|
|
42
281
|
secure: o.secure,
|
|
@@ -117,86 +356,16 @@ var defaultConsentTheme = createTheme({
|
|
|
117
356
|
}
|
|
118
357
|
});
|
|
119
358
|
|
|
120
|
-
// src/context/CategoriesContext.tsx
|
|
121
|
-
import * as React from "react";
|
|
122
|
-
import { jsx } from "react/jsx-runtime";
|
|
123
|
-
var CategoriesCtx = React.createContext([]);
|
|
124
|
-
function CategoriesProvider({
|
|
125
|
-
categories,
|
|
126
|
-
children
|
|
127
|
-
}) {
|
|
128
|
-
const value = React.useMemo(() => categories || [], [categories]);
|
|
129
|
-
return /* @__PURE__ */ jsx(CategoriesCtx.Provider, { value, children });
|
|
130
|
-
}
|
|
131
|
-
function useCustomCategories() {
|
|
132
|
-
return React.useContext(CategoriesCtx);
|
|
133
|
-
}
|
|
134
|
-
function useAllCategories() {
|
|
135
|
-
const customCategories = useCustomCategories();
|
|
136
|
-
return React.useMemo(() => {
|
|
137
|
-
const defaultCategories = [
|
|
138
|
-
{
|
|
139
|
-
id: "necessary",
|
|
140
|
-
name: "Cookies Necess\xE1rios",
|
|
141
|
-
description: "Essenciais para o funcionamento b\xE1sico do site. Incluem cookies de sess\xE3o, autentica\xE7\xE3o e seguran\xE7a.",
|
|
142
|
-
essential: true,
|
|
143
|
-
cookies: ["PHPSESSID", "JSESSIONID", "cookieConsent", "csrf_token"]
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
id: "analytics",
|
|
147
|
-
name: "Analytics e Estat\xEDsticas",
|
|
148
|
-
description: "Permitem medir audi\xEAncia e desempenho, gerando estat\xEDsticas an\xF4nimas de uso.",
|
|
149
|
-
essential: false,
|
|
150
|
-
cookies: ["_ga", "_ga_*", "_gid", "_gat", "gtag"]
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
id: "functional",
|
|
154
|
-
name: "Cookies Funcionais",
|
|
155
|
-
description: "Melhoram a experi\xEAncia do usu\xE1rio, lembrando prefer\xEAncias e configura\xE7\xF5es.",
|
|
156
|
-
essential: false,
|
|
157
|
-
cookies: ["language", "theme", "timezone", "preferences"]
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
id: "marketing",
|
|
161
|
-
name: "Marketing e Publicidade",
|
|
162
|
-
description: "Utilizados para publicidade direcionada e medi\xE7\xE3o de campanhas publicit\xE1rias.",
|
|
163
|
-
essential: false,
|
|
164
|
-
cookies: ["_fbp", "fr", "tr", "ads_*", "doubleclick"]
|
|
165
|
-
},
|
|
166
|
-
{
|
|
167
|
-
id: "social",
|
|
168
|
-
name: "Redes Sociais",
|
|
169
|
-
description: "Permitem compartilhamento e integra\xE7\xE3o com redes sociais como Facebook, YouTube, etc.",
|
|
170
|
-
essential: false,
|
|
171
|
-
cookies: ["__Secure-*", "sb", "datr", "c_user", "social_*"]
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
id: "personalization",
|
|
175
|
-
name: "Personaliza\xE7\xE3o",
|
|
176
|
-
description: "Adaptam o conte\xFAdo e interface \xE0s prefer\xEAncias individuais do usu\xE1rio.",
|
|
177
|
-
essential: false,
|
|
178
|
-
cookies: ["personalization_*", "content_*", "layout_*"]
|
|
179
|
-
}
|
|
180
|
-
];
|
|
181
|
-
return [...defaultCategories, ...customCategories];
|
|
182
|
-
}, [customCategories]);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
359
|
// src/context/ConsentContext.tsx
|
|
186
360
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
187
|
-
var PreferencesModal =
|
|
188
|
-
() => import("./PreferencesModal-
|
|
361
|
+
var PreferencesModal = React3.lazy(
|
|
362
|
+
() => import("./PreferencesModal-UL552BFP.js").then((m) => ({
|
|
189
363
|
default: m.PreferencesModal
|
|
190
364
|
}))
|
|
191
365
|
);
|
|
192
366
|
var DEFAULT_PREFERENCES = {
|
|
193
|
-
necessary: true
|
|
367
|
+
necessary: true
|
|
194
368
|
// Sempre ativo (essencial)
|
|
195
|
-
analytics: false,
|
|
196
|
-
functional: false,
|
|
197
|
-
marketing: false,
|
|
198
|
-
social: false,
|
|
199
|
-
personalization: false
|
|
200
369
|
};
|
|
201
370
|
function createInitialPreferences(customCategories) {
|
|
202
371
|
const prefs = { ...DEFAULT_PREFERENCES };
|
|
@@ -207,6 +376,18 @@ function createInitialPreferences(customCategories) {
|
|
|
207
376
|
}
|
|
208
377
|
return prefs;
|
|
209
378
|
}
|
|
379
|
+
function createFullConsentState(consented, preferences, source, isModalOpen = false, existingState) {
|
|
380
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
381
|
+
return {
|
|
382
|
+
version: "1.0",
|
|
383
|
+
consented,
|
|
384
|
+
preferences,
|
|
385
|
+
consentDate: existingState?.consentDate || now,
|
|
386
|
+
lastUpdate: now,
|
|
387
|
+
source,
|
|
388
|
+
isModalOpen
|
|
389
|
+
};
|
|
390
|
+
}
|
|
210
391
|
var DEFAULT_TEXTS = {
|
|
211
392
|
// Textos básicos
|
|
212
393
|
bannerMessage: "Utilizamos cookies para melhorar sua experi\xEAncia.",
|
|
@@ -243,11 +424,7 @@ function reducer(state, action) {
|
|
|
243
424
|
Object.keys(prefs).forEach((key) => {
|
|
244
425
|
prefs[key] = true;
|
|
245
426
|
});
|
|
246
|
-
return
|
|
247
|
-
consented: true,
|
|
248
|
-
preferences: prefs,
|
|
249
|
-
isModalOpen: false
|
|
250
|
-
};
|
|
427
|
+
return createFullConsentState(true, prefs, "banner", false, state);
|
|
251
428
|
}
|
|
252
429
|
case "REJECT_ALL": {
|
|
253
430
|
const prefs = createInitialPreferences(action.customCategories);
|
|
@@ -258,11 +435,7 @@ function reducer(state, action) {
|
|
|
258
435
|
}
|
|
259
436
|
});
|
|
260
437
|
}
|
|
261
|
-
return
|
|
262
|
-
consented: true,
|
|
263
|
-
preferences: prefs,
|
|
264
|
-
isModalOpen: false
|
|
265
|
-
};
|
|
438
|
+
return createFullConsentState(true, prefs, "banner", false, state);
|
|
266
439
|
}
|
|
267
440
|
case "SET_CATEGORY":
|
|
268
441
|
return {
|
|
@@ -270,42 +443,54 @@ function reducer(state, action) {
|
|
|
270
443
|
preferences: {
|
|
271
444
|
...state.preferences,
|
|
272
445
|
[action.category]: action.value
|
|
273
|
-
}
|
|
446
|
+
},
|
|
447
|
+
lastUpdate: (/* @__PURE__ */ new Date()).toISOString()
|
|
274
448
|
};
|
|
275
449
|
case "SET_PREFERENCES":
|
|
276
|
-
return
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
450
|
+
return createFullConsentState(
|
|
451
|
+
true,
|
|
452
|
+
action.preferences,
|
|
453
|
+
"modal",
|
|
454
|
+
false,
|
|
455
|
+
state
|
|
456
|
+
);
|
|
282
457
|
case "OPEN_MODAL":
|
|
283
458
|
return { ...state, isModalOpen: true };
|
|
284
459
|
case "CLOSE_MODAL":
|
|
285
|
-
return
|
|
286
|
-
|
|
460
|
+
return createFullConsentState(
|
|
461
|
+
true,
|
|
462
|
+
state.preferences,
|
|
463
|
+
"modal",
|
|
464
|
+
false,
|
|
465
|
+
state
|
|
466
|
+
);
|
|
287
467
|
case "RESET": {
|
|
288
|
-
return
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
468
|
+
return createFullConsentState(
|
|
469
|
+
false,
|
|
470
|
+
createInitialPreferences(action.customCategories),
|
|
471
|
+
"programmatic",
|
|
472
|
+
false
|
|
473
|
+
);
|
|
293
474
|
}
|
|
294
475
|
case "HYDRATE":
|
|
295
|
-
return { ...action.state };
|
|
476
|
+
return { ...action.state, isModalOpen: false };
|
|
477
|
+
// Nunca hidratar com modal aberto
|
|
296
478
|
default:
|
|
297
479
|
return state;
|
|
298
480
|
}
|
|
299
481
|
}
|
|
300
|
-
var StateCtx =
|
|
301
|
-
var ActionsCtx =
|
|
302
|
-
var TextsCtx =
|
|
303
|
-
var HydrationCtx =
|
|
482
|
+
var StateCtx = React3.createContext(null);
|
|
483
|
+
var ActionsCtx = React3.createContext(null);
|
|
484
|
+
var TextsCtx = React3.createContext(DEFAULT_TEXTS);
|
|
485
|
+
var HydrationCtx = React3.createContext(false);
|
|
304
486
|
function ConsentProvider({
|
|
305
487
|
initialState,
|
|
488
|
+
categories,
|
|
489
|
+
// NOVO: configuração completa de categorias
|
|
306
490
|
texts: textsProp,
|
|
307
491
|
theme,
|
|
308
492
|
customCategories,
|
|
493
|
+
// LEGACY: compatibilidade
|
|
309
494
|
scriptIntegrations,
|
|
310
495
|
PreferencesModalComponent,
|
|
311
496
|
preferencesModalProps = {},
|
|
@@ -316,29 +501,41 @@ function ConsentProvider({
|
|
|
316
501
|
cookie: cookieOpts,
|
|
317
502
|
children
|
|
318
503
|
}) {
|
|
319
|
-
const texts =
|
|
504
|
+
const texts = React3.useMemo(
|
|
320
505
|
() => ({ ...DEFAULT_TEXTS, ...textsProp ?? {} }),
|
|
321
506
|
[textsProp]
|
|
322
507
|
);
|
|
323
|
-
const cookie =
|
|
508
|
+
const cookie = React3.useMemo(
|
|
324
509
|
() => ({ ...DEFAULT_COOKIE_OPTS, ...cookieOpts ?? {} }),
|
|
325
510
|
[cookieOpts]
|
|
326
511
|
);
|
|
327
|
-
const appliedTheme =
|
|
512
|
+
const appliedTheme = React3.useMemo(
|
|
328
513
|
() => theme || defaultConsentTheme,
|
|
329
514
|
[theme]
|
|
330
515
|
);
|
|
331
|
-
const
|
|
516
|
+
const finalCategoriesConfig = React3.useMemo(() => {
|
|
517
|
+
if (categories) return categories;
|
|
518
|
+
if (customCategories) {
|
|
519
|
+
return {
|
|
520
|
+
enabledCategories: ["analytics"],
|
|
521
|
+
// padrão quando usando API antiga
|
|
522
|
+
customCategories
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
return void 0;
|
|
526
|
+
}, [categories, customCategories]);
|
|
527
|
+
const boot = React3.useMemo(() => {
|
|
332
528
|
if (initialState) return { ...initialState, isModalOpen: false };
|
|
333
|
-
return
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
529
|
+
return createFullConsentState(
|
|
530
|
+
false,
|
|
531
|
+
createInitialPreferences(customCategories),
|
|
532
|
+
"banner",
|
|
533
|
+
false
|
|
534
|
+
);
|
|
338
535
|
}, [initialState, customCategories]);
|
|
339
|
-
const [state, dispatch] =
|
|
340
|
-
const [isHydrated, setIsHydrated] =
|
|
341
|
-
|
|
536
|
+
const [state, dispatch] = React3.useReducer(reducer, boot);
|
|
537
|
+
const [isHydrated, setIsHydrated] = React3.useState(false);
|
|
538
|
+
React3.useEffect(() => {
|
|
342
539
|
if (!initialState) {
|
|
343
540
|
const saved = readConsentCookie(cookie.name);
|
|
344
541
|
if (saved?.consented) {
|
|
@@ -348,24 +545,24 @@ function ConsentProvider({
|
|
|
348
545
|
}
|
|
349
546
|
setIsHydrated(true);
|
|
350
547
|
}, [cookie.name, initialState]);
|
|
351
|
-
|
|
352
|
-
if (state.consented) writeConsentCookie(state, cookie);
|
|
548
|
+
React3.useEffect(() => {
|
|
549
|
+
if (state.consented) writeConsentCookie(state, state.source, cookie);
|
|
353
550
|
}, [state, cookie]);
|
|
354
|
-
const prevConsented =
|
|
355
|
-
|
|
551
|
+
const prevConsented = React3.useRef(state.consented);
|
|
552
|
+
React3.useEffect(() => {
|
|
356
553
|
if (!prevConsented.current && state.consented && onConsentGiven) {
|
|
357
554
|
setTimeout(() => onConsentGiven(state), 150);
|
|
358
555
|
}
|
|
359
556
|
prevConsented.current = state.consented;
|
|
360
557
|
}, [state, onConsentGiven]);
|
|
361
|
-
const prevPrefs =
|
|
362
|
-
|
|
558
|
+
const prevPrefs = React3.useRef(state.preferences);
|
|
559
|
+
React3.useEffect(() => {
|
|
363
560
|
if (state.consented && onPreferencesSaved && prevPrefs.current !== state.preferences) {
|
|
364
561
|
setTimeout(() => onPreferencesSaved(state.preferences), 150);
|
|
365
562
|
prevPrefs.current = state.preferences;
|
|
366
563
|
}
|
|
367
564
|
}, [state, onPreferencesSaved]);
|
|
368
|
-
const api =
|
|
565
|
+
const api = React3.useMemo(() => {
|
|
369
566
|
const acceptAll = () => dispatch({ type: "ACCEPT_ALL", customCategories });
|
|
370
567
|
const rejectAll = () => dispatch({ type: "REJECT_ALL", customCategories });
|
|
371
568
|
const setPreference = (category, value) => dispatch({ type: "SET_CATEGORY", category, value });
|
|
@@ -389,29 +586,36 @@ function ConsentProvider({
|
|
|
389
586
|
resetConsent
|
|
390
587
|
};
|
|
391
588
|
}, [state, cookie, customCategories]);
|
|
392
|
-
return /* @__PURE__ */ jsx2(ThemeProvider, { theme: appliedTheme, children: /* @__PURE__ */ jsx2(StateCtx.Provider, { value: state, children: /* @__PURE__ */ jsx2(ActionsCtx.Provider, { value: api, children: /* @__PURE__ */ jsx2(TextsCtx.Provider, { value: texts, children: /* @__PURE__ */ jsx2(HydrationCtx.Provider, { value: isHydrated, children: /* @__PURE__ */ jsxs(
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
589
|
+
return /* @__PURE__ */ jsx2(ThemeProvider, { theme: appliedTheme, children: /* @__PURE__ */ jsx2(StateCtx.Provider, { value: state, children: /* @__PURE__ */ jsx2(ActionsCtx.Provider, { value: api, children: /* @__PURE__ */ jsx2(TextsCtx.Provider, { value: texts, children: /* @__PURE__ */ jsx2(HydrationCtx.Provider, { value: isHydrated, children: /* @__PURE__ */ jsxs(
|
|
590
|
+
CategoriesProvider,
|
|
591
|
+
{
|
|
592
|
+
config: finalCategoriesConfig,
|
|
593
|
+
categories: customCategories,
|
|
594
|
+
children: [
|
|
595
|
+
children,
|
|
596
|
+
!disableAutomaticModal && /* @__PURE__ */ jsx2(React3.Suspense, { fallback: null, children: PreferencesModalComponent ? /* @__PURE__ */ jsx2(PreferencesModalComponent, { ...preferencesModalProps }) : /* @__PURE__ */ jsx2(PreferencesModal, { hideBranding }) })
|
|
597
|
+
]
|
|
598
|
+
}
|
|
599
|
+
) }) }) }) }) });
|
|
396
600
|
}
|
|
397
601
|
function useConsentStateInternal() {
|
|
398
|
-
const ctx =
|
|
602
|
+
const ctx = React3.useContext(StateCtx);
|
|
399
603
|
if (!ctx)
|
|
400
604
|
throw new Error("useConsentState must be used within ConsentProvider");
|
|
401
605
|
return ctx;
|
|
402
606
|
}
|
|
403
607
|
function useConsentActionsInternal() {
|
|
404
|
-
const ctx =
|
|
608
|
+
const ctx = React3.useContext(ActionsCtx);
|
|
405
609
|
if (!ctx)
|
|
406
610
|
throw new Error("useConsentActions must be used within ConsentProvider");
|
|
407
611
|
return ctx;
|
|
408
612
|
}
|
|
409
613
|
function useConsentTextsInternal() {
|
|
410
|
-
const ctx =
|
|
614
|
+
const ctx = React3.useContext(TextsCtx);
|
|
411
615
|
return ctx;
|
|
412
616
|
}
|
|
413
617
|
function useConsentHydrationInternal() {
|
|
414
|
-
return
|
|
618
|
+
return React3.useContext(HydrationCtx);
|
|
415
619
|
}
|
|
416
620
|
|
|
417
621
|
// src/hooks/useConsent.ts
|
|
@@ -505,12 +709,25 @@ function PreferencesModal2({
|
|
|
505
709
|
}) {
|
|
506
710
|
const { preferences, setPreferences, closePreferences, isModalOpen } = useConsent();
|
|
507
711
|
const texts = useConsentTexts();
|
|
508
|
-
const
|
|
509
|
-
|
|
712
|
+
const { toggleableCategories } = useCategories();
|
|
713
|
+
const [tempPreferences, setTempPreferences] = useState2(
|
|
714
|
+
() => {
|
|
715
|
+
const initialPrefs = { necessary: true };
|
|
716
|
+
toggleableCategories.forEach((category) => {
|
|
717
|
+
initialPrefs[category.id] = preferences[category.id] ?? false;
|
|
718
|
+
});
|
|
719
|
+
return initialPrefs;
|
|
720
|
+
}
|
|
721
|
+
);
|
|
722
|
+
useEffect4(() => {
|
|
510
723
|
if (isModalOpen) {
|
|
511
|
-
|
|
724
|
+
const syncedPrefs = { necessary: true };
|
|
725
|
+
toggleableCategories.forEach((category) => {
|
|
726
|
+
syncedPrefs[category.id] = preferences[category.id] ?? false;
|
|
727
|
+
});
|
|
728
|
+
setTempPreferences(syncedPrefs);
|
|
512
729
|
}
|
|
513
|
-
}, [isModalOpen, preferences]);
|
|
730
|
+
}, [isModalOpen, preferences, toggleableCategories]);
|
|
514
731
|
const open = DialogProps2?.open ?? isModalOpen ?? false;
|
|
515
732
|
const handleSave = () => {
|
|
516
733
|
setPreferences(tempPreferences);
|
|
@@ -531,38 +748,23 @@ function PreferencesModal2({
|
|
|
531
748
|
/* @__PURE__ */ jsxs3(DialogContent, { dividers: true, children: [
|
|
532
749
|
/* @__PURE__ */ jsx4(Typography2, { variant: "body2", sx: { mb: 2 }, children: texts.modalIntro }),
|
|
533
750
|
/* @__PURE__ */ jsxs3(FormGroup, { children: [
|
|
534
|
-
/* @__PURE__ */ jsx4(
|
|
535
|
-
FormControlLabel,
|
|
536
|
-
{
|
|
537
|
-
control: /* @__PURE__ */ jsx4(
|
|
538
|
-
Switch,
|
|
539
|
-
{
|
|
540
|
-
checked: tempPreferences.analytics,
|
|
541
|
-
onChange: (e) => setTempPreferences((prev) => ({
|
|
542
|
-
...prev,
|
|
543
|
-
analytics: e.target.checked
|
|
544
|
-
}))
|
|
545
|
-
}
|
|
546
|
-
),
|
|
547
|
-
label: "Cookies Anal\xEDticos (medem uso do site)"
|
|
548
|
-
}
|
|
549
|
-
),
|
|
550
|
-
/* @__PURE__ */ jsx4(
|
|
751
|
+
toggleableCategories.map((category) => /* @__PURE__ */ jsx4(
|
|
551
752
|
FormControlLabel,
|
|
552
753
|
{
|
|
553
754
|
control: /* @__PURE__ */ jsx4(
|
|
554
755
|
Switch,
|
|
555
756
|
{
|
|
556
|
-
checked: tempPreferences.
|
|
757
|
+
checked: tempPreferences[category.id] ?? false,
|
|
557
758
|
onChange: (e) => setTempPreferences((prev) => ({
|
|
558
759
|
...prev,
|
|
559
|
-
|
|
760
|
+
[category.id]: e.target.checked
|
|
560
761
|
}))
|
|
561
762
|
}
|
|
562
763
|
),
|
|
563
|
-
label:
|
|
564
|
-
}
|
|
565
|
-
|
|
764
|
+
label: `${category.name} - ${category.description}`
|
|
765
|
+
},
|
|
766
|
+
category.id
|
|
767
|
+
)),
|
|
566
768
|
/* @__PURE__ */ jsx4(
|
|
567
769
|
FormControlLabel,
|
|
568
770
|
{
|
|
@@ -584,8 +786,11 @@ function PreferencesModal2({
|
|
|
584
786
|
|
|
585
787
|
export {
|
|
586
788
|
defaultConsentTheme,
|
|
789
|
+
DEFAULT_PROJECT_CATEGORIES,
|
|
790
|
+
analyzeDeveloperConfiguration,
|
|
791
|
+
useCategories,
|
|
792
|
+
useCategoryStatus,
|
|
587
793
|
useCustomCategories,
|
|
588
|
-
useAllCategories,
|
|
589
794
|
Branding,
|
|
590
795
|
PreferencesModal2 as PreferencesModal,
|
|
591
796
|
ConsentProvider,
|