@vocoweb/kernel 1.0.0
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/LICENSE +21 -0
- package/README.md +252 -0
- package/dist/index.d.mts +2670 -0
- package/dist/index.d.ts +2670 -0
- package/dist/index.js +5560 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +5370 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react-CSDXc0m4.d.mts +446 -0
- package/dist/react-CSDXc0m4.d.ts +446 -0
- package/dist/react.d.mts +3 -0
- package/dist/react.d.ts +3 -0
- package/dist/react.js +1854 -0
- package/dist/react.js.map +1 -0
- package/dist/react.mjs +1845 -0
- package/dist/react.mjs.map +1 -0
- package/package.json +89 -0
package/dist/react.js
ADDED
|
@@ -0,0 +1,1854 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var supabaseJs = require('@supabase/supabase-js');
|
|
5
|
+
var zod = require('zod');
|
|
6
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
7
|
+
|
|
8
|
+
// src/auth/components/VocoAuth.tsx
|
|
9
|
+
|
|
10
|
+
// src/shared/errors.ts
|
|
11
|
+
var VocoError = class extends Error {
|
|
12
|
+
constructor(message, code, statusCode = 500, details) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.statusCode = statusCode;
|
|
16
|
+
this.details = details;
|
|
17
|
+
this.name = "VocoError";
|
|
18
|
+
Error.captureStackTrace(this, this.constructor);
|
|
19
|
+
}
|
|
20
|
+
toJSON() {
|
|
21
|
+
return {
|
|
22
|
+
name: this.name,
|
|
23
|
+
message: this.message,
|
|
24
|
+
code: this.code,
|
|
25
|
+
statusCode: this.statusCode,
|
|
26
|
+
details: this.details
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var VocoAuthError = class _VocoAuthError extends VocoError {
|
|
31
|
+
constructor(message, code = "AUTH_ERROR", details) {
|
|
32
|
+
super(message, code, 401, details);
|
|
33
|
+
this.name = "VocoAuthError";
|
|
34
|
+
}
|
|
35
|
+
static invalidCredentials() {
|
|
36
|
+
return new _VocoAuthError("Invalid email or password", "INVALID_CREDENTIALS");
|
|
37
|
+
}
|
|
38
|
+
static sessionExpired() {
|
|
39
|
+
return new _VocoAuthError("Session has expired", "SESSION_EXPIRED");
|
|
40
|
+
}
|
|
41
|
+
static unauthorized(message = "Unauthorized access") {
|
|
42
|
+
return new _VocoAuthError(message, "UNAUTHORIZED");
|
|
43
|
+
}
|
|
44
|
+
static forbidden(message = "Access forbidden") {
|
|
45
|
+
const error = new _VocoAuthError(message, "FORBIDDEN");
|
|
46
|
+
error.statusCode = 403;
|
|
47
|
+
return error;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var VocoConfigError = class _VocoConfigError extends VocoError {
|
|
51
|
+
constructor(message, code = "CONFIG_ERROR", details) {
|
|
52
|
+
super(message, code, 500, details);
|
|
53
|
+
this.name = "VocoConfigError";
|
|
54
|
+
}
|
|
55
|
+
static missingEnvVar(varName) {
|
|
56
|
+
return new _VocoConfigError(
|
|
57
|
+
`Missing required environment variable: ${varName}`,
|
|
58
|
+
"MISSING_ENV_VAR",
|
|
59
|
+
{ varName }
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
static invalidConfig(message) {
|
|
63
|
+
return new _VocoConfigError(`Invalid configuration: ${message}`, "INVALID_CONFIG");
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// src/config/env.ts
|
|
68
|
+
var SupabaseConfigSchema = zod.z.object({
|
|
69
|
+
url: zod.z.string().url("SUPABASE_URL must be a valid URL"),
|
|
70
|
+
anonKey: zod.z.string().min(1, "SUPABASE_ANON_KEY is required"),
|
|
71
|
+
serviceRoleKey: zod.z.string().min(1, "SUPABASE_SERVICE_ROLE_KEY is required")
|
|
72
|
+
});
|
|
73
|
+
var StripeConfigSchema = zod.z.object({
|
|
74
|
+
secretKey: zod.z.string().startsWith("sk_", "STRIPE_SECRET_KEY must start with sk_"),
|
|
75
|
+
publishableKey: zod.z.string().startsWith("pk_", "STRIPE_PUBLISHABLE_KEY must start with pk_"),
|
|
76
|
+
webhookSecret: zod.z.string().optional()
|
|
77
|
+
});
|
|
78
|
+
var AppConfigSchema = zod.z.object({
|
|
79
|
+
url: zod.z.string().url("NEXT_PUBLIC_APP_URL must be a valid URL").optional().default("http://localhost:3000"),
|
|
80
|
+
name: zod.z.string().optional().default("VocoWeb App"),
|
|
81
|
+
supportEmail: zod.z.string().email("SUPPORT_EMAIL must be valid").optional().default("support@vocoweb.in"),
|
|
82
|
+
legalEmail: zod.z.string().email("LEGAL_EMAIL must be valid").optional().default("legal@vocoweb.in")
|
|
83
|
+
});
|
|
84
|
+
var ResidencyConfigSchema = zod.z.object({
|
|
85
|
+
enabled: zod.z.boolean().optional().default(false),
|
|
86
|
+
region: zod.z.enum(["eu", "us", "apac", "global"]).optional().default("global"),
|
|
87
|
+
strictMode: zod.z.boolean().optional().default(false)
|
|
88
|
+
});
|
|
89
|
+
var AccessibilityConfigSchema = zod.z.object({
|
|
90
|
+
enforceContrast: zod.z.boolean().optional().default(true),
|
|
91
|
+
enforceAriaLabels: zod.z.boolean().optional().default(true),
|
|
92
|
+
enforceKeyboardNav: zod.z.boolean().optional().default(true),
|
|
93
|
+
contrastRatio: zod.z.number().min(1).max(21).optional().default(4.5)
|
|
94
|
+
});
|
|
95
|
+
var KernelConfigSchema = zod.z.object({
|
|
96
|
+
supabase: SupabaseConfigSchema,
|
|
97
|
+
stripe: StripeConfigSchema,
|
|
98
|
+
app: AppConfigSchema,
|
|
99
|
+
residency: ResidencyConfigSchema,
|
|
100
|
+
accessibility: AccessibilityConfigSchema
|
|
101
|
+
});
|
|
102
|
+
function getEnvVar(key) {
|
|
103
|
+
if (typeof window !== "undefined") {
|
|
104
|
+
return window?.env?.[key] || undefined?.[key] || "";
|
|
105
|
+
}
|
|
106
|
+
return process.env[key] || "";
|
|
107
|
+
}
|
|
108
|
+
function validateConfig(config) {
|
|
109
|
+
try {
|
|
110
|
+
return KernelConfigSchema.parse(config);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
if (error instanceof zod.z.ZodError) {
|
|
113
|
+
const errors = error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
|
|
114
|
+
throw new VocoConfigError(`Configuration validation failed: ${errors}`, "CONFIG_VALIDATION_FAILED", {
|
|
115
|
+
errors: error.errors
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function loadConfigFromEnv() {
|
|
122
|
+
const rawConfig = {
|
|
123
|
+
supabase: {
|
|
124
|
+
url: getEnvVar("NEXT_PUBLIC_SUPABASE_URL") || getEnvVar("SUPABASE_URL"),
|
|
125
|
+
anonKey: getEnvVar("NEXT_PUBLIC_SUPABASE_ANON_KEY") || getEnvVar("SUPABASE_ANON_KEY"),
|
|
126
|
+
serviceRoleKey: getEnvVar("SUPABASE_SERVICE_ROLE_KEY")
|
|
127
|
+
},
|
|
128
|
+
stripe: {
|
|
129
|
+
secretKey: getEnvVar("STRIPE_SECRET_KEY"),
|
|
130
|
+
publishableKey: getEnvVar("NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY") || getEnvVar("STRIPE_PUBLISHABLE_KEY"),
|
|
131
|
+
webhookSecret: getEnvVar("STRIPE_WEBHOOK_SECRET") || void 0
|
|
132
|
+
},
|
|
133
|
+
app: {
|
|
134
|
+
url: getEnvVar("NEXT_PUBLIC_APP_URL") || void 0,
|
|
135
|
+
name: getEnvVar("NEXT_PUBLIC_APP_NAME") || void 0,
|
|
136
|
+
supportEmail: getEnvVar("SUPPORT_EMAIL") || void 0,
|
|
137
|
+
legalEmail: getEnvVar("LEGAL_EMAIL") || void 0
|
|
138
|
+
},
|
|
139
|
+
residency: {
|
|
140
|
+
enabled: getEnvVar("VOCO_DATA_RESIDENCY_ENABLED") === "true",
|
|
141
|
+
region: getEnvVar("VOCO_DATA_REGION") || void 0,
|
|
142
|
+
strictMode: getEnvVar("VOCO_RESIDENCY_STRICT_MODE") === "true"
|
|
143
|
+
},
|
|
144
|
+
accessibility: {
|
|
145
|
+
enforceContrast: getEnvVar("VOCO_ENFORCE_CONTRAST") !== "false",
|
|
146
|
+
enforceAriaLabels: getEnvVar("VOCO_ENFORCE_ARIA") !== "false",
|
|
147
|
+
enforceKeyboardNav: getEnvVar("VOCO_ENFORCE_KEYBOARD") !== "false",
|
|
148
|
+
contrastRatio: getEnvVar("VOCO_CONTRAST_RATIO") ? parseFloat(getEnvVar("VOCO_CONTRAST_RATIO")) : void 0
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
return validateConfig(rawConfig);
|
|
152
|
+
}
|
|
153
|
+
var cachedConfig = null;
|
|
154
|
+
function getConfig() {
|
|
155
|
+
if (!cachedConfig) {
|
|
156
|
+
cachedConfig = loadConfigFromEnv();
|
|
157
|
+
}
|
|
158
|
+
return cachedConfig;
|
|
159
|
+
}
|
|
160
|
+
function getSupabaseConfig() {
|
|
161
|
+
return getConfig().supabase;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/auth/config.ts
|
|
165
|
+
var browserClient = null;
|
|
166
|
+
function getBrowserClient() {
|
|
167
|
+
if (browserClient) {
|
|
168
|
+
return browserClient;
|
|
169
|
+
}
|
|
170
|
+
if (typeof window === "undefined") {
|
|
171
|
+
throw new VocoConfigError(
|
|
172
|
+
"Browser client can only be used in browser environment",
|
|
173
|
+
"INVALID_CLIENT_TYPE"
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
const config = getSupabaseConfig();
|
|
177
|
+
browserClient = supabaseJs.createClient(config.url, config.anonKey, {
|
|
178
|
+
auth: {
|
|
179
|
+
persistSession: true,
|
|
180
|
+
autoRefreshToken: true,
|
|
181
|
+
detectSessionInUrl: true,
|
|
182
|
+
storage: typeof window !== "undefined" ? window.localStorage : void 0,
|
|
183
|
+
storageKey: "vocoweb-auth-token",
|
|
184
|
+
flowType: "pkce"
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
return browserClient;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// src/auth/client.ts
|
|
191
|
+
function handleAuthError(error) {
|
|
192
|
+
if (!error) {
|
|
193
|
+
throw new VocoAuthError("Unknown authentication error", "UNKNOWN_ERROR");
|
|
194
|
+
}
|
|
195
|
+
const errorMap = {
|
|
196
|
+
InvalidCredentials: "INVALID_CREDENTIALS",
|
|
197
|
+
EmailNotConfirmed: "EMAIL_NOT_CONFIRMED",
|
|
198
|
+
UserNotFound: "USER_NOT_FOUND",
|
|
199
|
+
InvalidEmail: "INVALID_EMAIL",
|
|
200
|
+
WeakPassword: "WEAK_PASSWORD",
|
|
201
|
+
EmailAlreadyExists: "EMAIL_ALREADY_EXISTS",
|
|
202
|
+
SessionExpired: "SESSION_EXPIRED"
|
|
203
|
+
};
|
|
204
|
+
const code = errorMap[error.name] || "AUTH_ERROR";
|
|
205
|
+
throw new VocoAuthError(error.message || "Authentication failed", code, {
|
|
206
|
+
originalError: error.name
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
async function loginWithOAuth(options = {}) {
|
|
210
|
+
const {
|
|
211
|
+
provider = "google",
|
|
212
|
+
redirectTo,
|
|
213
|
+
scopes,
|
|
214
|
+
queryParams,
|
|
215
|
+
skipBrowserRedirect = false
|
|
216
|
+
} = options;
|
|
217
|
+
const supabase = getBrowserClient();
|
|
218
|
+
let redirectUrl = redirectTo;
|
|
219
|
+
if (!redirectUrl && typeof window !== "undefined") {
|
|
220
|
+
redirectUrl = `${window.location.origin}/auth/callback`;
|
|
221
|
+
}
|
|
222
|
+
const { error } = await supabase.auth.signInWithOAuth({
|
|
223
|
+
provider,
|
|
224
|
+
options: {
|
|
225
|
+
redirectTo: redirectUrl,
|
|
226
|
+
scopes,
|
|
227
|
+
queryParams,
|
|
228
|
+
skipBrowserRedirect
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
if (error) {
|
|
232
|
+
handleAuthError(error);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
async function loginWithGoogle(redirectTo) {
|
|
236
|
+
await loginWithOAuth({ provider: "google", redirectTo });
|
|
237
|
+
}
|
|
238
|
+
async function loginWithGitHub(redirectTo) {
|
|
239
|
+
await loginWithOAuth({ provider: "github", redirectTo });
|
|
240
|
+
}
|
|
241
|
+
async function signInWithPassword(options) {
|
|
242
|
+
const supabase = getBrowserClient();
|
|
243
|
+
const { data, error } = await supabase.auth.signInWithPassword({
|
|
244
|
+
email: options.email,
|
|
245
|
+
password: options.password
|
|
246
|
+
});
|
|
247
|
+
if (error) {
|
|
248
|
+
handleAuthError(error);
|
|
249
|
+
}
|
|
250
|
+
return { data };
|
|
251
|
+
}
|
|
252
|
+
async function signUp(options) {
|
|
253
|
+
const supabase = getBrowserClient();
|
|
254
|
+
const { data, error } = await supabase.auth.signUp({
|
|
255
|
+
email: options.email,
|
|
256
|
+
password: options.password,
|
|
257
|
+
options: {
|
|
258
|
+
data: options.metadata
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
if (error) {
|
|
262
|
+
handleAuthError(error);
|
|
263
|
+
}
|
|
264
|
+
return { data };
|
|
265
|
+
}
|
|
266
|
+
function VocoAuth({
|
|
267
|
+
redirectUrl,
|
|
268
|
+
className,
|
|
269
|
+
mode = "both",
|
|
270
|
+
providers = ["google"],
|
|
271
|
+
showSignUp = false,
|
|
272
|
+
onSuccess,
|
|
273
|
+
onError
|
|
274
|
+
}) {
|
|
275
|
+
const [isLoading, setIsLoading] = react.useState(false);
|
|
276
|
+
const [error, setError] = react.useState(null);
|
|
277
|
+
const [authMode, setAuthMode] = react.useState("signIn");
|
|
278
|
+
const handleOAuthLogin = react.useCallback(
|
|
279
|
+
async (provider) => {
|
|
280
|
+
setIsLoading(true);
|
|
281
|
+
setError(null);
|
|
282
|
+
try {
|
|
283
|
+
if (provider === "google") {
|
|
284
|
+
await loginWithGoogle(redirectUrl);
|
|
285
|
+
} else {
|
|
286
|
+
await loginWithGitHub(redirectUrl);
|
|
287
|
+
}
|
|
288
|
+
} catch (err) {
|
|
289
|
+
const errorMessage = err instanceof Error ? err.message : "Authentication failed";
|
|
290
|
+
setError(errorMessage);
|
|
291
|
+
setIsLoading(false);
|
|
292
|
+
onError?.({ message: errorMessage, code: "OAUTH_ERROR" });
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
[redirectUrl, onError]
|
|
296
|
+
);
|
|
297
|
+
const handlePasswordAuth = react.useCallback(
|
|
298
|
+
async (e) => {
|
|
299
|
+
e.preventDefault();
|
|
300
|
+
setIsLoading(true);
|
|
301
|
+
setError(null);
|
|
302
|
+
const formData = new FormData(e.currentTarget);
|
|
303
|
+
const email = formData.get("email");
|
|
304
|
+
const password = formData.get("password");
|
|
305
|
+
try {
|
|
306
|
+
let result;
|
|
307
|
+
if (authMode === "signIn") {
|
|
308
|
+
result = await signInWithPassword({ email, password });
|
|
309
|
+
} else {
|
|
310
|
+
result = await signUp({
|
|
311
|
+
email,
|
|
312
|
+
password
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
if (result.data.user) {
|
|
316
|
+
onSuccess?.({
|
|
317
|
+
id: result.data.user.id,
|
|
318
|
+
email: result.data.user.email || ""
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
if (redirectUrl && typeof window !== "undefined") {
|
|
322
|
+
window.location.href = redirectUrl;
|
|
323
|
+
}
|
|
324
|
+
} catch (err) {
|
|
325
|
+
const errorMessage = err instanceof Error ? err.message : "Authentication failed";
|
|
326
|
+
setError(errorMessage);
|
|
327
|
+
setIsLoading(false);
|
|
328
|
+
onError?.({ message: errorMessage, code: "AUTH_ERROR" });
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
[authMode, redirectUrl, onSuccess, onError]
|
|
332
|
+
);
|
|
333
|
+
const toggleAuthMode = () => {
|
|
334
|
+
setAuthMode((prev) => prev === "signIn" ? "signUp" : "signIn");
|
|
335
|
+
setError(null);
|
|
336
|
+
};
|
|
337
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
338
|
+
"div",
|
|
339
|
+
{
|
|
340
|
+
className,
|
|
341
|
+
style: {
|
|
342
|
+
display: "flex",
|
|
343
|
+
flexDirection: "column",
|
|
344
|
+
alignItems: "center",
|
|
345
|
+
justifyContent: "center",
|
|
346
|
+
minHeight: "400px",
|
|
347
|
+
padding: "2rem"
|
|
348
|
+
},
|
|
349
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
350
|
+
"div",
|
|
351
|
+
{
|
|
352
|
+
style: {
|
|
353
|
+
width: "100%",
|
|
354
|
+
maxWidth: "400px",
|
|
355
|
+
backgroundColor: "white",
|
|
356
|
+
borderRadius: "12px",
|
|
357
|
+
padding: "2rem",
|
|
358
|
+
boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)"
|
|
359
|
+
},
|
|
360
|
+
children: [
|
|
361
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center", marginBottom: "2rem" }, children: [
|
|
362
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
363
|
+
"h1",
|
|
364
|
+
{
|
|
365
|
+
style: {
|
|
366
|
+
fontSize: "1.5rem",
|
|
367
|
+
fontWeight: "700",
|
|
368
|
+
color: "#111827",
|
|
369
|
+
marginBottom: "0.5rem"
|
|
370
|
+
},
|
|
371
|
+
children: authMode === "signIn" ? "Sign in to your account" : "Create your account"
|
|
372
|
+
}
|
|
373
|
+
),
|
|
374
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.875rem", color: "#6b7280" }, children: authMode === "signIn" ? "Welcome back!" : "Get started for free" })
|
|
375
|
+
] }),
|
|
376
|
+
error && /* @__PURE__ */ jsxRuntime.jsx(
|
|
377
|
+
"div",
|
|
378
|
+
{
|
|
379
|
+
role: "alert",
|
|
380
|
+
style: {
|
|
381
|
+
padding: "0.75rem",
|
|
382
|
+
marginBottom: "1rem",
|
|
383
|
+
backgroundColor: "#fef2f2",
|
|
384
|
+
border: "1px solid #fecaca",
|
|
385
|
+
borderRadius: "0.5rem",
|
|
386
|
+
color: "#991b1b",
|
|
387
|
+
fontSize: "0.875rem"
|
|
388
|
+
},
|
|
389
|
+
children: error
|
|
390
|
+
}
|
|
391
|
+
),
|
|
392
|
+
(mode === "oauth" || mode === "both") && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.75rem", marginBottom: "1.5rem" }, children: [
|
|
393
|
+
providers.includes("google") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
394
|
+
"button",
|
|
395
|
+
{
|
|
396
|
+
type: "button",
|
|
397
|
+
onClick: () => handleOAuthLogin("google"),
|
|
398
|
+
disabled: isLoading,
|
|
399
|
+
"aria-label": "Sign in with Google",
|
|
400
|
+
style: {
|
|
401
|
+
display: "flex",
|
|
402
|
+
alignItems: "center",
|
|
403
|
+
justifyContent: "center",
|
|
404
|
+
gap: "0.75rem",
|
|
405
|
+
width: "100%",
|
|
406
|
+
padding: "0.75rem 1rem",
|
|
407
|
+
backgroundColor: "white",
|
|
408
|
+
border: "1px solid #d1d5db",
|
|
409
|
+
borderRadius: "0.5rem",
|
|
410
|
+
fontSize: "0.875rem",
|
|
411
|
+
fontWeight: "500",
|
|
412
|
+
color: "#374151",
|
|
413
|
+
cursor: isLoading ? "not-allowed" : "pointer",
|
|
414
|
+
opacity: isLoading ? 0.5 : 1,
|
|
415
|
+
transition: "all 0.15s ease"
|
|
416
|
+
},
|
|
417
|
+
onMouseEnter: (e) => {
|
|
418
|
+
if (!isLoading) {
|
|
419
|
+
e.currentTarget.style.backgroundColor = "#f9fafb";
|
|
420
|
+
}
|
|
421
|
+
},
|
|
422
|
+
onMouseLeave: (e) => {
|
|
423
|
+
e.currentTarget.style.backgroundColor = "white";
|
|
424
|
+
},
|
|
425
|
+
children: [
|
|
426
|
+
/* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 20 20", "aria-hidden": "true", children: [
|
|
427
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
428
|
+
"path",
|
|
429
|
+
{
|
|
430
|
+
d: "M19.6 10.23c0-.82-.1-1.42-.25-2.05H10v3.72h5.5c-.15.96-.74 2.31-2.04 3.22v2.45h3.16c1.89-1.73 2.98-4.3 2.98-7.34z",
|
|
431
|
+
fill: "#4285F4"
|
|
432
|
+
}
|
|
433
|
+
),
|
|
434
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
435
|
+
"path",
|
|
436
|
+
{
|
|
437
|
+
d: "M10 20c2.7 0 4.96-.89 6.62-2.42l-3.16-2.45c-.86.58-1.97.92-3.46.92-2.65 0-4.92-1.8-5.73-4.22H2.98v2.52C4.56 17.64 7.06 20 10 20z",
|
|
438
|
+
fill: "#34A853"
|
|
439
|
+
}
|
|
440
|
+
),
|
|
441
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
442
|
+
"path",
|
|
443
|
+
{
|
|
444
|
+
d: "M4.27 11.83c-.2-.58-.32-1.19-.32-1.83 0-.64.12-1.25.32-1.83V4.9H2.98C1.08 7.28 1.08 10.72 2.98 13.1l1.29-1.27z",
|
|
445
|
+
fill: "#FBBC05"
|
|
446
|
+
}
|
|
447
|
+
),
|
|
448
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
449
|
+
"path",
|
|
450
|
+
{
|
|
451
|
+
d: "M10 3.98c1.52 0 2.87.52 3.94 1.54l2.9-2.9C15.06 1.19 12.7 0 10 0 7.06 0 4.56 2.36 3.38 5.9l1.29 1.27C5.38 4.98 7.65 3.98 10 3.98z",
|
|
452
|
+
fill: "#EA4335"
|
|
453
|
+
}
|
|
454
|
+
)
|
|
455
|
+
] }),
|
|
456
|
+
"Continue with Google"
|
|
457
|
+
]
|
|
458
|
+
}
|
|
459
|
+
),
|
|
460
|
+
providers.includes("github") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
461
|
+
"button",
|
|
462
|
+
{
|
|
463
|
+
type: "button",
|
|
464
|
+
onClick: () => handleOAuthLogin("github"),
|
|
465
|
+
disabled: isLoading,
|
|
466
|
+
"aria-label": "Sign in with GitHub",
|
|
467
|
+
style: {
|
|
468
|
+
display: "flex",
|
|
469
|
+
alignItems: "center",
|
|
470
|
+
justifyContent: "center",
|
|
471
|
+
gap: "0.75rem",
|
|
472
|
+
width: "100%",
|
|
473
|
+
padding: "0.75rem 1rem",
|
|
474
|
+
backgroundColor: "white",
|
|
475
|
+
border: "1px solid #d1d5db",
|
|
476
|
+
borderRadius: "0.5rem",
|
|
477
|
+
fontSize: "0.875rem",
|
|
478
|
+
fontWeight: "500",
|
|
479
|
+
color: "#374151",
|
|
480
|
+
cursor: isLoading ? "not-allowed" : "pointer",
|
|
481
|
+
opacity: isLoading ? 0.5 : 1,
|
|
482
|
+
transition: "all 0.15s ease"
|
|
483
|
+
},
|
|
484
|
+
onMouseEnter: (e) => {
|
|
485
|
+
if (!isLoading) {
|
|
486
|
+
e.currentTarget.style.backgroundColor = "#f9fafb";
|
|
487
|
+
}
|
|
488
|
+
},
|
|
489
|
+
onMouseLeave: (e) => {
|
|
490
|
+
e.currentTarget.style.backgroundColor = "white";
|
|
491
|
+
},
|
|
492
|
+
children: [
|
|
493
|
+
/* @__PURE__ */ jsxRuntime.jsx("svg", { width: "20", height: "20", fill: "currentColor", viewBox: "0 0 20 20", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
494
|
+
"path",
|
|
495
|
+
{
|
|
496
|
+
fillRule: "evenodd",
|
|
497
|
+
d: "M10 0C4.477 0 0 4.484 0 10.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0110 4.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0020 10.017C20 4.484 15.522 0 10 0z",
|
|
498
|
+
clipRule: "evenodd"
|
|
499
|
+
}
|
|
500
|
+
) }),
|
|
501
|
+
"Continue with GitHub"
|
|
502
|
+
]
|
|
503
|
+
}
|
|
504
|
+
)
|
|
505
|
+
] }),
|
|
506
|
+
(mode === "both" || mode === "password" && showSignUp) && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
507
|
+
"div",
|
|
508
|
+
{
|
|
509
|
+
style: {
|
|
510
|
+
display: "flex",
|
|
511
|
+
alignItems: "center",
|
|
512
|
+
gap: "1rem",
|
|
513
|
+
marginBottom: "1.5rem"
|
|
514
|
+
},
|
|
515
|
+
children: [
|
|
516
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, height: "1px", backgroundColor: "#e5e7eb" } }),
|
|
517
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.875rem", color: "#6b7280" }, children: "or continue with email" }),
|
|
518
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, height: "1px", backgroundColor: "#e5e7eb" } })
|
|
519
|
+
]
|
|
520
|
+
}
|
|
521
|
+
),
|
|
522
|
+
(mode === "password" || mode === "both") && /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handlePasswordAuth, style: { display: "flex", flexDirection: "column", gap: "1rem" }, children: [
|
|
523
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
524
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
525
|
+
"label",
|
|
526
|
+
{
|
|
527
|
+
htmlFor: "email",
|
|
528
|
+
style: {
|
|
529
|
+
display: "block",
|
|
530
|
+
fontSize: "0.875rem",
|
|
531
|
+
fontWeight: "500",
|
|
532
|
+
color: "#374151",
|
|
533
|
+
marginBottom: "0.5rem"
|
|
534
|
+
},
|
|
535
|
+
children: "Email address"
|
|
536
|
+
}
|
|
537
|
+
),
|
|
538
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
539
|
+
"input",
|
|
540
|
+
{
|
|
541
|
+
id: "email",
|
|
542
|
+
name: "email",
|
|
543
|
+
type: "email",
|
|
544
|
+
autoComplete: "email",
|
|
545
|
+
required: true,
|
|
546
|
+
disabled: isLoading,
|
|
547
|
+
style: {
|
|
548
|
+
width: "100%",
|
|
549
|
+
padding: "0.75rem 1rem",
|
|
550
|
+
border: "1px solid #d1d5db",
|
|
551
|
+
borderRadius: "0.5rem",
|
|
552
|
+
fontSize: "0.875rem",
|
|
553
|
+
outline: "none",
|
|
554
|
+
cursor: isLoading ? "not-allowed" : "text"
|
|
555
|
+
},
|
|
556
|
+
onFocus: (e) => {
|
|
557
|
+
e.currentTarget.style.borderColor = "#3b82f6";
|
|
558
|
+
e.currentTarget.style.boxShadow = "0 0 0 3px rgba(59, 130, 246, 0.1)";
|
|
559
|
+
},
|
|
560
|
+
onBlur: (e) => {
|
|
561
|
+
e.currentTarget.style.borderColor = "#d1d5db";
|
|
562
|
+
e.currentTarget.style.boxShadow = "none";
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
)
|
|
566
|
+
] }),
|
|
567
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
568
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
569
|
+
"label",
|
|
570
|
+
{
|
|
571
|
+
htmlFor: "password",
|
|
572
|
+
style: {
|
|
573
|
+
display: "block",
|
|
574
|
+
fontSize: "0.875rem",
|
|
575
|
+
fontWeight: "500",
|
|
576
|
+
color: "#374151",
|
|
577
|
+
marginBottom: "0.5rem"
|
|
578
|
+
},
|
|
579
|
+
children: "Password"
|
|
580
|
+
}
|
|
581
|
+
),
|
|
582
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
583
|
+
"input",
|
|
584
|
+
{
|
|
585
|
+
id: "password",
|
|
586
|
+
name: "password",
|
|
587
|
+
type: "password",
|
|
588
|
+
autoComplete: authMode === "signIn" ? "current-password" : "new-password",
|
|
589
|
+
required: true,
|
|
590
|
+
disabled: isLoading,
|
|
591
|
+
minLength: 6,
|
|
592
|
+
style: {
|
|
593
|
+
width: "100%",
|
|
594
|
+
padding: "0.75rem 1rem",
|
|
595
|
+
border: "1px solid #d1d5db",
|
|
596
|
+
borderRadius: "0.5rem",
|
|
597
|
+
fontSize: "0.875rem",
|
|
598
|
+
outline: "none",
|
|
599
|
+
cursor: isLoading ? "not-allowed" : "text"
|
|
600
|
+
},
|
|
601
|
+
onFocus: (e) => {
|
|
602
|
+
e.currentTarget.style.borderColor = "#3b82f6";
|
|
603
|
+
e.currentTarget.style.boxShadow = "0 0 0 3px rgba(59, 130, 246, 0.1)";
|
|
604
|
+
},
|
|
605
|
+
onBlur: (e) => {
|
|
606
|
+
e.currentTarget.style.borderColor = "#d1d5db";
|
|
607
|
+
e.currentTarget.style.boxShadow = "none";
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
)
|
|
611
|
+
] }),
|
|
612
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
613
|
+
"button",
|
|
614
|
+
{
|
|
615
|
+
type: "submit",
|
|
616
|
+
disabled: isLoading,
|
|
617
|
+
style: {
|
|
618
|
+
width: "100%",
|
|
619
|
+
padding: "0.75rem 1rem",
|
|
620
|
+
backgroundColor: "#3b82f6",
|
|
621
|
+
border: "none",
|
|
622
|
+
borderRadius: "0.5rem",
|
|
623
|
+
fontSize: "0.875rem",
|
|
624
|
+
fontWeight: "500",
|
|
625
|
+
color: "white",
|
|
626
|
+
cursor: isLoading ? "not-allowed" : "pointer",
|
|
627
|
+
opacity: isLoading ? 0.5 : 1,
|
|
628
|
+
transition: "all 0.15s ease"
|
|
629
|
+
},
|
|
630
|
+
onMouseEnter: (e) => {
|
|
631
|
+
if (!isLoading) {
|
|
632
|
+
e.currentTarget.style.backgroundColor = "#2563eb";
|
|
633
|
+
}
|
|
634
|
+
},
|
|
635
|
+
onMouseLeave: (e) => {
|
|
636
|
+
e.currentTarget.style.backgroundColor = "#3b82f6";
|
|
637
|
+
},
|
|
638
|
+
children: isLoading ? "Loading..." : authMode === "signIn" ? "Sign in" : "Create account"
|
|
639
|
+
}
|
|
640
|
+
)
|
|
641
|
+
] }),
|
|
642
|
+
showSignUp && mode !== "oauth" && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: "1.5rem", textAlign: "center" }, children: /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { fontSize: "0.875rem", color: "#6b7280" }, children: [
|
|
643
|
+
authMode === "signIn" ? "Don't have an account? " : "Already have an account? ",
|
|
644
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
645
|
+
"button",
|
|
646
|
+
{
|
|
647
|
+
type: "button",
|
|
648
|
+
onClick: toggleAuthMode,
|
|
649
|
+
disabled: isLoading,
|
|
650
|
+
style: {
|
|
651
|
+
background: "none",
|
|
652
|
+
border: "none",
|
|
653
|
+
color: "#3b82f6",
|
|
654
|
+
fontSize: "0.875rem",
|
|
655
|
+
fontWeight: "500",
|
|
656
|
+
cursor: isLoading ? "not-allowed" : "pointer",
|
|
657
|
+
textDecoration: "underline"
|
|
658
|
+
},
|
|
659
|
+
children: authMode === "signIn" ? "Sign up" : "Sign in"
|
|
660
|
+
}
|
|
661
|
+
)
|
|
662
|
+
] }) }),
|
|
663
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { marginTop: "1.5rem", fontSize: "0.75rem", color: "#9ca3af", textAlign: "center" }, children: "By continuing, you agree to our Terms of Service and Privacy Policy." })
|
|
664
|
+
]
|
|
665
|
+
}
|
|
666
|
+
)
|
|
667
|
+
}
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
var DEFAULT_SECTIONS = {
|
|
671
|
+
dataCollection: {
|
|
672
|
+
title: "1. Information We Collect",
|
|
673
|
+
content: `We collect information you provide directly to us, including:
|
|
674
|
+
- Name, email address, and other contact information
|
|
675
|
+
- Account credentials (encrypted)
|
|
676
|
+
- Profile information and preferences
|
|
677
|
+
- Usage data and analytics
|
|
678
|
+
- Payment information (processed securely through third-party providers)`
|
|
679
|
+
},
|
|
680
|
+
dataUsage: {
|
|
681
|
+
title: "2. How We Use Your Information",
|
|
682
|
+
content: `We use the information we collect to:
|
|
683
|
+
- Provide, maintain, and improve our services
|
|
684
|
+
- Process transactions and send related information
|
|
685
|
+
- Send technical notices and support messages
|
|
686
|
+
- Respond to comments and questions
|
|
687
|
+
- Monitor and analyze trends, usage, and activities
|
|
688
|
+
- Detect, prevent, and address technical issues and security threats`
|
|
689
|
+
},
|
|
690
|
+
dataSharing: {
|
|
691
|
+
title: "3. Information Sharing",
|
|
692
|
+
content: `We do not sell your personal data. We may share your information with:
|
|
693
|
+
- Service providers who perform services on our behalf
|
|
694
|
+
- Payment processors (for transaction processing)
|
|
695
|
+
- Analytics providers (to understand service usage)
|
|
696
|
+
- Legal authorities when required by law
|
|
697
|
+
- Third parties with your explicit consent`
|
|
698
|
+
},
|
|
699
|
+
dataRetention: {
|
|
700
|
+
title: "4. Data Retention",
|
|
701
|
+
content: `We retain your personal data for as long as necessary to provide our services and fulfill the transactions you request. You may request deletion of your data at any time, subject to certain legal obligations.`
|
|
702
|
+
},
|
|
703
|
+
userRights: {
|
|
704
|
+
title: "5. Your Rights (GDPR)",
|
|
705
|
+
content: `Under GDPR, you have the right to:
|
|
706
|
+
- Access your personal data
|
|
707
|
+
- Correct inaccurate data
|
|
708
|
+
- Request deletion of your data ("right to be forgotten")
|
|
709
|
+
- Object to processing of your data
|
|
710
|
+
- Data portability
|
|
711
|
+
- Restrict processing
|
|
712
|
+
- Withdraw consent at any time`
|
|
713
|
+
},
|
|
714
|
+
cookies: {
|
|
715
|
+
title: "6. Cookies and Tracking",
|
|
716
|
+
content: `We use cookies and similar technologies to:
|
|
717
|
+
- Remember your preferences
|
|
718
|
+
- Understand how you use our services
|
|
719
|
+
- Provide analytics and improve performance
|
|
720
|
+
- Deliver personalized content`
|
|
721
|
+
},
|
|
722
|
+
security: {
|
|
723
|
+
title: "7. Security",
|
|
724
|
+
content: `We implement appropriate technical and organizational measures to protect your personal data against unauthorized access, alteration, disclosure, or destruction. However, no method of transmission over the internet is 100% secure.`
|
|
725
|
+
},
|
|
726
|
+
contact: {
|
|
727
|
+
title: "8. Contact Us",
|
|
728
|
+
content: `For questions about this privacy policy or your personal data, please contact us.`
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
function PrivacyPolicy({
|
|
732
|
+
companyName,
|
|
733
|
+
email,
|
|
734
|
+
updatedAt,
|
|
735
|
+
websiteUrl,
|
|
736
|
+
supportEmail,
|
|
737
|
+
retentionDays = 365,
|
|
738
|
+
dataCategories = ["personal information", "usage data", "payment information"],
|
|
739
|
+
sections = {},
|
|
740
|
+
className
|
|
741
|
+
}) {
|
|
742
|
+
const formatDate = (date) => {
|
|
743
|
+
if (typeof date === "string") {
|
|
744
|
+
return date;
|
|
745
|
+
}
|
|
746
|
+
const parts = date.toISOString().split("T");
|
|
747
|
+
return parts[0];
|
|
748
|
+
};
|
|
749
|
+
const allSections = { ...DEFAULT_SECTIONS, ...sections };
|
|
750
|
+
const lastUpdated = updatedAt ? formatDate(updatedAt) : (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
751
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { fontFamily: "system-ui, -apple-system, sans-serif" }, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { maxWidth: "800px", margin: "0 auto", padding: "2rem 1rem" }, children: [
|
|
752
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { style: { fontSize: "2rem", fontWeight: "700", marginBottom: "0.5rem" }, children: "Privacy Policy" }),
|
|
753
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: { color: "#6b7280", marginBottom: "2rem" }, children: [
|
|
754
|
+
"Last Updated: ",
|
|
755
|
+
lastUpdated
|
|
756
|
+
] }),
|
|
757
|
+
/* @__PURE__ */ jsxRuntime.jsx("section", { style: { marginBottom: "2rem" }, children: /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { lineHeight: "1.6" }, children: [
|
|
758
|
+
"At ",
|
|
759
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: companyName }),
|
|
760
|
+
' ("we", "our", or "us"), we are committed to protecting your privacy and personal data. This privacy policy explains how we collect, use, share, and protect your information when you use our services.'
|
|
761
|
+
] }) }),
|
|
762
|
+
Object.entries(allSections).map(([key, section]) => /* @__PURE__ */ jsxRuntime.jsxs("section", { style: { marginBottom: "2rem" }, children: [
|
|
763
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: { fontSize: "1.25rem", fontWeight: "600", marginBottom: "1rem" }, children: section.title }),
|
|
764
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { lineHeight: "1.7", whiteSpace: "pre-line", color: "#374151" }, children: section.content })
|
|
765
|
+
] }, key)),
|
|
766
|
+
dataCategories && dataCategories.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("section", { style: { marginBottom: "2rem" }, children: [
|
|
767
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: { fontSize: "1.25rem", fontWeight: "600", marginBottom: "1rem" }, children: "Categories of Personal Data" }),
|
|
768
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { style: { paddingLeft: "1.5rem", color: "#374151", lineHeight: "1.7" }, children: dataCategories.map((category, index) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: category }, index)) })
|
|
769
|
+
] }),
|
|
770
|
+
/* @__PURE__ */ jsxRuntime.jsxs("section", { style: { marginBottom: "2rem" }, children: [
|
|
771
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: { fontSize: "1.25rem", fontWeight: "600", marginBottom: "1rem" }, children: "Data Retention Period" }),
|
|
772
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: { lineHeight: "1.7", color: "#374151" }, children: [
|
|
773
|
+
"We retain your personal data for a period of ",
|
|
774
|
+
/* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
|
|
775
|
+
retentionDays,
|
|
776
|
+
" days"
|
|
777
|
+
] }),
|
|
778
|
+
" after account closure, unless longer retention is required by law."
|
|
779
|
+
] })
|
|
780
|
+
] }),
|
|
781
|
+
/* @__PURE__ */ jsxRuntime.jsxs("section", { style: { marginBottom: "2rem" }, children: [
|
|
782
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: { fontSize: "1.25rem", fontWeight: "600", marginBottom: "1rem" }, children: "Contact Information" }),
|
|
783
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { lineHeight: "1.7", color: "#374151" }, children: [
|
|
784
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
785
|
+
"If you have any questions about this Privacy Policy, please contact us at:",
|
|
786
|
+
" ",
|
|
787
|
+
/* @__PURE__ */ jsxRuntime.jsx("a", { href: `mailto:${email}`, style: { color: "#3b82f6" }, children: email })
|
|
788
|
+
] }),
|
|
789
|
+
supportEmail && supportEmail !== email && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { marginTop: "0.5rem" }, children: [
|
|
790
|
+
"For support inquiries:",
|
|
791
|
+
" ",
|
|
792
|
+
/* @__PURE__ */ jsxRuntime.jsx("a", { href: `mailto:${supportEmail}`, style: { color: "#3b82f6" }, children: supportEmail })
|
|
793
|
+
] }),
|
|
794
|
+
websiteUrl && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { marginTop: "0.5rem" }, children: [
|
|
795
|
+
"Website: ",
|
|
796
|
+
" ",
|
|
797
|
+
/* @__PURE__ */ jsxRuntime.jsx("a", { href: websiteUrl, style: { color: "#3b82f6" }, children: websiteUrl })
|
|
798
|
+
] })
|
|
799
|
+
] })
|
|
800
|
+
] }),
|
|
801
|
+
/* @__PURE__ */ jsxRuntime.jsx("footer", { style: { marginTop: "3rem", paddingTop: "2rem", borderTop: "1px solid #e5e7eb" }, children: /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { fontSize: "0.875rem", color: "#6b7280", textAlign: "center" }, children: [
|
|
802
|
+
"\xA9 ",
|
|
803
|
+
(/* @__PURE__ */ new Date()).getFullYear(),
|
|
804
|
+
" ",
|
|
805
|
+
companyName,
|
|
806
|
+
". All rights reserved."
|
|
807
|
+
] }) })
|
|
808
|
+
] }) });
|
|
809
|
+
}
|
|
810
|
+
function TermsOfService({
|
|
811
|
+
companyName,
|
|
812
|
+
email,
|
|
813
|
+
updatedAt,
|
|
814
|
+
websiteUrl,
|
|
815
|
+
jurisdiction = "The laws of the jurisdiction where the company is registered",
|
|
816
|
+
governingLaw,
|
|
817
|
+
limitationOfLiability = true,
|
|
818
|
+
intellectualProperty = true,
|
|
819
|
+
terminationClause = true,
|
|
820
|
+
className
|
|
821
|
+
}) {
|
|
822
|
+
const formatDate = (date) => {
|
|
823
|
+
if (typeof date === "string") {
|
|
824
|
+
return date;
|
|
825
|
+
}
|
|
826
|
+
const parts = date.toISOString().split("T");
|
|
827
|
+
return parts[0];
|
|
828
|
+
};
|
|
829
|
+
const lastUpdated = updatedAt ? formatDate(updatedAt) : (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
830
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { fontFamily: "system-ui, -apple-system, sans-serif" }, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { maxWidth: "800px", margin: "0 auto", padding: "2rem 1rem" }, children: [
|
|
831
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { style: { fontSize: "2rem", fontWeight: "700", marginBottom: "0.5rem" }, children: "Terms of Service" }),
|
|
832
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: { color: "#6b7280", marginBottom: "2rem" }, children: [
|
|
833
|
+
"Last Updated: ",
|
|
834
|
+
lastUpdated
|
|
835
|
+
] }),
|
|
836
|
+
/* @__PURE__ */ jsxRuntime.jsx("section", { style: { marginBottom: "2rem" }, children: /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { lineHeight: "1.6" }, children: [
|
|
837
|
+
"Welcome to ",
|
|
838
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: companyName }),
|
|
839
|
+
". By using our services, you agree to these terms. Please read them carefully."
|
|
840
|
+
] }) }),
|
|
841
|
+
/* @__PURE__ */ jsxRuntime.jsxs("section", { style: { marginBottom: "2rem" }, children: [
|
|
842
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: { fontSize: "1.25rem", fontWeight: "600", marginBottom: "1rem" }, children: "1. Acceptance of Terms" }),
|
|
843
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: { lineHeight: "1.7", color: "#374151" }, children: [
|
|
844
|
+
"By accessing or using ",
|
|
845
|
+
companyName,
|
|
846
|
+
"'s services, you agree to be bound by these Terms of Service. If you do not agree to these terms, please do not use our services."
|
|
847
|
+
] })
|
|
848
|
+
] }),
|
|
849
|
+
/* @__PURE__ */ jsxRuntime.jsxs("section", { style: { marginBottom: "2rem" }, children: [
|
|
850
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: { fontSize: "1.25rem", fontWeight: "600", marginBottom: "1rem" }, children: "2. Description of Service" }),
|
|
851
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: { lineHeight: "1.7", color: "#374151" }, children: [
|
|
852
|
+
companyName,
|
|
853
|
+
" provides software services and related features as described in our documentation. We reserve the right to modify, suspend, or discontinue any aspect of our services at any time."
|
|
854
|
+
] })
|
|
855
|
+
] }),
|
|
856
|
+
/* @__PURE__ */ jsxRuntime.jsxs("section", { style: { marginBottom: "2rem" }, children: [
|
|
857
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: { fontSize: "1.25rem", fontWeight: "600", marginBottom: "1rem" }, children: "3. User Responsibilities" }),
|
|
858
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { lineHeight: "1.7", color: "#374151" }, children: "As a user of our services, you agree to:" }),
|
|
859
|
+
/* @__PURE__ */ jsxRuntime.jsxs("ul", { style: { paddingLeft: "1.5rem", color: "#374151", lineHeight: "1.7" }, children: [
|
|
860
|
+
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "Provide accurate and complete information" }),
|
|
861
|
+
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "Maintain the security of your account credentials" }),
|
|
862
|
+
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "Comply with all applicable laws and regulations" }),
|
|
863
|
+
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "Not use our services for any unlawful purpose" }),
|
|
864
|
+
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "Not interfere with or disrupt our services" }),
|
|
865
|
+
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "Not attempt to gain unauthorized access to our systems" })
|
|
866
|
+
] })
|
|
867
|
+
] }),
|
|
868
|
+
intellectualProperty && /* @__PURE__ */ jsxRuntime.jsxs("section", { style: { marginBottom: "2rem" }, children: [
|
|
869
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: { fontSize: "1.25rem", fontWeight: "600", marginBottom: "1rem" }, children: "4. Intellectual Property" }),
|
|
870
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: { lineHeight: "1.7", color: "#374151" }, children: [
|
|
871
|
+
"All content, features, and functionality of our services are owned by ",
|
|
872
|
+
companyName,
|
|
873
|
+
" and are protected by copyright, trademark, and other intellectual property laws. You may not:"
|
|
874
|
+
] }),
|
|
875
|
+
/* @__PURE__ */ jsxRuntime.jsxs("ul", { style: { paddingLeft: "1.5rem", color: "#374151", lineHeight: "1.7" }, children: [
|
|
876
|
+
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "Reproduce, distribute, or create derivative works" }),
|
|
877
|
+
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "Use our services for commercial purposes without authorization" }),
|
|
878
|
+
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "Remove any proprietary notices from our services" })
|
|
879
|
+
] })
|
|
880
|
+
] }),
|
|
881
|
+
/* @__PURE__ */ jsxRuntime.jsxs("section", { style: { marginBottom: "2rem" }, children: [
|
|
882
|
+
/* @__PURE__ */ jsxRuntime.jsxs("h2", { style: { fontSize: "1.25rem", fontWeight: "600", marginBottom: "1rem" }, children: [
|
|
883
|
+
intellectualProperty ? "5" : "4",
|
|
884
|
+
". User Content"
|
|
885
|
+
] }),
|
|
886
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: { lineHeight: "1.7", color: "#374151" }, children: [
|
|
887
|
+
"You retain ownership of any content you submit to our services. By submitting content, you grant",
|
|
888
|
+
companyName,
|
|
889
|
+
" a license to use, modify, and display your content for the purpose of providing our services. You represent that you have all necessary rights to grant such license."
|
|
890
|
+
] })
|
|
891
|
+
] }),
|
|
892
|
+
terminationClause && /* @__PURE__ */ jsxRuntime.jsxs("section", { style: { marginBottom: "2rem" }, children: [
|
|
893
|
+
/* @__PURE__ */ jsxRuntime.jsxs("h2", { style: { fontSize: "1.25rem", fontWeight: "600", marginBottom: "1rem" }, children: [
|
|
894
|
+
intellectualProperty ? "6" : "5",
|
|
895
|
+
". Termination"
|
|
896
|
+
] }),
|
|
897
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { lineHeight: "1.7", color: "#374151" }, children: "We may terminate or suspend your account and access to our services at any time, with or without cause, with or without notice. Upon termination, your right to use our services will immediately cease." })
|
|
898
|
+
] }),
|
|
899
|
+
limitationOfLiability && /* @__PURE__ */ jsxRuntime.jsxs("section", { style: { marginBottom: "2rem" }, children: [
|
|
900
|
+
/* @__PURE__ */ jsxRuntime.jsxs("h2", { style: { fontSize: "1.25rem", fontWeight: "600", marginBottom: "1rem" }, children: [
|
|
901
|
+
terminationClause ? "7" : intellectualProperty ? "6" : "5",
|
|
902
|
+
". Limitation of Liability"
|
|
903
|
+
] }),
|
|
904
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: { lineHeight: "1.7", color: "#374151" }, children: [
|
|
905
|
+
"To the fullest extent permitted by law, ",
|
|
906
|
+
companyName,
|
|
907
|
+
" shall not be liable for any indirect, incidental, special, consequential, or punitive damages, including without limitation, loss of profits, data, use, goodwill, or other intangible losses."
|
|
908
|
+
] })
|
|
909
|
+
] }),
|
|
910
|
+
/* @__PURE__ */ jsxRuntime.jsxs("section", { style: { marginBottom: "2rem" }, children: [
|
|
911
|
+
/* @__PURE__ */ jsxRuntime.jsxs("h2", { style: { fontSize: "1.25rem", fontWeight: "600", marginBottom: "1rem" }, children: [
|
|
912
|
+
limitationOfLiability ? "8" : terminationClause ? "7" : intellectualProperty ? "6" : "5",
|
|
913
|
+
". Governing Law"
|
|
914
|
+
] }),
|
|
915
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: { lineHeight: "1.7", color: "#374151" }, children: [
|
|
916
|
+
"These terms shall be governed by and construed in accordance with ",
|
|
917
|
+
governingLaw || jurisdiction,
|
|
918
|
+
"."
|
|
919
|
+
] })
|
|
920
|
+
] }),
|
|
921
|
+
/* @__PURE__ */ jsxRuntime.jsxs("section", { style: { marginBottom: "2rem" }, children: [
|
|
922
|
+
/* @__PURE__ */ jsxRuntime.jsxs("h2", { style: { fontSize: "1.25rem", fontWeight: "600", marginBottom: "1rem" }, children: [
|
|
923
|
+
limitationOfLiability ? "9" : terminationClause ? "8" : intellectualProperty ? "7" : "6",
|
|
924
|
+
". Changes to Terms"
|
|
925
|
+
] }),
|
|
926
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { lineHeight: "1.7", color: "#374151" }, children: "We reserve the right to modify these terms at any time. We will notify users of any material changes via email or through our services. Your continued use of our services after such modifications constitutes your acceptance of the updated terms." })
|
|
927
|
+
] }),
|
|
928
|
+
/* @__PURE__ */ jsxRuntime.jsxs("section", { style: { marginBottom: "2rem" }, children: [
|
|
929
|
+
/* @__PURE__ */ jsxRuntime.jsxs("h2", { style: { fontSize: "1.25rem", fontWeight: "600", marginBottom: "1rem" }, children: [
|
|
930
|
+
limitationOfLiability ? "10" : terminationClause ? "9" : intellectualProperty ? "8" : "7",
|
|
931
|
+
". Contact Us"
|
|
932
|
+
] }),
|
|
933
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: { lineHeight: "1.7", color: "#374151" }, children: [
|
|
934
|
+
"If you have any questions about these Terms of Service, please contact us at:",
|
|
935
|
+
" ",
|
|
936
|
+
/* @__PURE__ */ jsxRuntime.jsx("a", { href: `mailto:${email}`, style: { color: "#3b82f6" }, children: email })
|
|
937
|
+
] }),
|
|
938
|
+
websiteUrl && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { marginTop: "0.5rem", lineHeight: "1.7", color: "#374151" }, children: [
|
|
939
|
+
"Website: ",
|
|
940
|
+
" ",
|
|
941
|
+
/* @__PURE__ */ jsxRuntime.jsx("a", { href: websiteUrl, style: { color: "#3b82f6" }, children: websiteUrl })
|
|
942
|
+
] })
|
|
943
|
+
] }),
|
|
944
|
+
/* @__PURE__ */ jsxRuntime.jsx("footer", { style: { marginTop: "3rem", paddingTop: "2rem", borderTop: "1px solid #e5e7eb" }, children: /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { fontSize: "0.875rem", color: "#6b7280", textAlign: "center" }, children: [
|
|
945
|
+
"\xA9 ",
|
|
946
|
+
(/* @__PURE__ */ new Date()).getFullYear(),
|
|
947
|
+
" ",
|
|
948
|
+
companyName,
|
|
949
|
+
". All rights reserved."
|
|
950
|
+
] }) })
|
|
951
|
+
] }) });
|
|
952
|
+
}
|
|
953
|
+
var STORAGE_KEY = "vocoweb-cookie-consent";
|
|
954
|
+
var CONSENT_EXPIRY_DAYS = 365;
|
|
955
|
+
var DEFAULT_CATEGORIES = [
|
|
956
|
+
{
|
|
957
|
+
id: "necessary",
|
|
958
|
+
name: "Strictly Necessary",
|
|
959
|
+
description: "These cookies are essential for the site to function properly.",
|
|
960
|
+
required: true
|
|
961
|
+
},
|
|
962
|
+
{
|
|
963
|
+
id: "analytics",
|
|
964
|
+
name: "Analytics",
|
|
965
|
+
description: "Help us improve our website by collecting anonymous usage data.",
|
|
966
|
+
required: false,
|
|
967
|
+
scripts: ["https://www.googletagmanager.com/gtag/js"]
|
|
968
|
+
},
|
|
969
|
+
{
|
|
970
|
+
id: "marketing",
|
|
971
|
+
name: "Marketing",
|
|
972
|
+
description: "Used to deliver advertisements that are relevant to you and your interests.",
|
|
973
|
+
required: false
|
|
974
|
+
},
|
|
975
|
+
{
|
|
976
|
+
id: "preferences",
|
|
977
|
+
name: "Preferences",
|
|
978
|
+
description: "Allows the site to remember your choices and preferences.",
|
|
979
|
+
required: false
|
|
980
|
+
}
|
|
981
|
+
];
|
|
982
|
+
function CookieConsent({
|
|
983
|
+
onAccept,
|
|
984
|
+
onReject,
|
|
985
|
+
onCustomize,
|
|
986
|
+
position = "bottom",
|
|
987
|
+
style = "banner",
|
|
988
|
+
categories = DEFAULT_CATEGORIES,
|
|
989
|
+
privacyPolicyUrl,
|
|
990
|
+
acceptAllText = "Accept All",
|
|
991
|
+
rejectAllText = "Reject All",
|
|
992
|
+
customizeText = "Customize",
|
|
993
|
+
className
|
|
994
|
+
}) {
|
|
995
|
+
const [isVisible, setIsVisible] = react.useState(false);
|
|
996
|
+
const [showCustomize, setShowCustomize] = react.useState(false);
|
|
997
|
+
const [selectedCategories, setSelectedCategories] = react.useState({});
|
|
998
|
+
react.useEffect(() => {
|
|
999
|
+
try {
|
|
1000
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
1001
|
+
if (stored) {
|
|
1002
|
+
const { timestamp, consent } = JSON.parse(stored);
|
|
1003
|
+
const daysSince = (Date.now() - timestamp) / (1e3 * 60 * 60 * 24);
|
|
1004
|
+
if (daysSince > CONSENT_EXPIRY_DAYS) {
|
|
1005
|
+
setIsVisible(true);
|
|
1006
|
+
} else if (consent === "pending") {
|
|
1007
|
+
setIsVisible(true);
|
|
1008
|
+
}
|
|
1009
|
+
} else {
|
|
1010
|
+
setIsVisible(true);
|
|
1011
|
+
}
|
|
1012
|
+
} catch {
|
|
1013
|
+
setIsVisible(true);
|
|
1014
|
+
}
|
|
1015
|
+
}, []);
|
|
1016
|
+
react.useEffect(() => {
|
|
1017
|
+
const required = {};
|
|
1018
|
+
categories.forEach((cat) => {
|
|
1019
|
+
if (cat.required) {
|
|
1020
|
+
required[cat.id] = true;
|
|
1021
|
+
}
|
|
1022
|
+
});
|
|
1023
|
+
setSelectedCategories(required);
|
|
1024
|
+
}, [categories]);
|
|
1025
|
+
const saveConsent = react.useCallback((consent, categories2) => {
|
|
1026
|
+
const data = {
|
|
1027
|
+
timestamp: Date.now(),
|
|
1028
|
+
consent,
|
|
1029
|
+
categories: categories2
|
|
1030
|
+
};
|
|
1031
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
|
1032
|
+
}, []);
|
|
1033
|
+
const handleAccept = react.useCallback(() => {
|
|
1034
|
+
const allAccepted = {};
|
|
1035
|
+
categories.forEach((cat) => {
|
|
1036
|
+
allAccepted[cat.id] = true;
|
|
1037
|
+
});
|
|
1038
|
+
setSelectedCategories(allAccepted);
|
|
1039
|
+
saveConsent("accept", allAccepted);
|
|
1040
|
+
setIsVisible(false);
|
|
1041
|
+
onAccept?.();
|
|
1042
|
+
}, [categories, onAccept, saveConsent]);
|
|
1043
|
+
const handleReject = react.useCallback(() => {
|
|
1044
|
+
const requiredOnly = {};
|
|
1045
|
+
categories.forEach((cat) => {
|
|
1046
|
+
if (cat.required) {
|
|
1047
|
+
requiredOnly[cat.id] = true;
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
setSelectedCategories(requiredOnly);
|
|
1051
|
+
saveConsent("reject", requiredOnly);
|
|
1052
|
+
setIsVisible(false);
|
|
1053
|
+
onReject?.();
|
|
1054
|
+
}, [categories, onReject, saveConsent]);
|
|
1055
|
+
const handleCustomize = react.useCallback(() => {
|
|
1056
|
+
setShowCustomize(true);
|
|
1057
|
+
}, []);
|
|
1058
|
+
const handleSaveCustom = react.useCallback(() => {
|
|
1059
|
+
saveConsent("partial", selectedCategories);
|
|
1060
|
+
setIsVisible(false);
|
|
1061
|
+
setShowCustomize(false);
|
|
1062
|
+
onCustomize?.(selectedCategories);
|
|
1063
|
+
}, [selectedCategories, saveConsent, onCustomize]);
|
|
1064
|
+
const toggleCategory = react.useCallback((categoryId) => {
|
|
1065
|
+
const category = categories.find((c) => c.id === categoryId);
|
|
1066
|
+
if (category?.required) {
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
setSelectedCategories((prev) => ({
|
|
1070
|
+
...prev,
|
|
1071
|
+
[categoryId]: !prev[categoryId]
|
|
1072
|
+
}));
|
|
1073
|
+
}, [categories]);
|
|
1074
|
+
const handleCloseModal = react.useCallback(() => {
|
|
1075
|
+
setShowCustomize(false);
|
|
1076
|
+
}, []);
|
|
1077
|
+
react.useEffect(() => {
|
|
1078
|
+
if (Object.keys(selectedCategories).length === 0) {
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
categories.forEach((category) => {
|
|
1082
|
+
if (category.scripts && category.scripts.length > 0) {
|
|
1083
|
+
const shouldBlock = !selectedCategories[category.id];
|
|
1084
|
+
category.scripts.forEach((scriptSrc) => {
|
|
1085
|
+
const scripts = document.querySelectorAll(`script[src="${scriptSrc}"]`);
|
|
1086
|
+
scripts.forEach((script) => {
|
|
1087
|
+
if (shouldBlock) {
|
|
1088
|
+
script.remove();
|
|
1089
|
+
}
|
|
1090
|
+
});
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1094
|
+
}, [selectedCategories, categories]);
|
|
1095
|
+
if (!isVisible) {
|
|
1096
|
+
return null;
|
|
1097
|
+
}
|
|
1098
|
+
if (style === "banner" && !showCustomize) {
|
|
1099
|
+
const isBottom = position === "bottom";
|
|
1100
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1101
|
+
"div",
|
|
1102
|
+
{
|
|
1103
|
+
className,
|
|
1104
|
+
role: "dialog",
|
|
1105
|
+
"aria-labelledby": "cookie-consent-title",
|
|
1106
|
+
"aria-describedby": "cookie-consent-description",
|
|
1107
|
+
style: {
|
|
1108
|
+
position: "fixed",
|
|
1109
|
+
left: 0,
|
|
1110
|
+
right: 0,
|
|
1111
|
+
[isBottom ? "bottom" : "top"]: 0,
|
|
1112
|
+
backgroundColor: "rgba(17, 24, 39, 0.95)",
|
|
1113
|
+
backdropFilter: "blur(8px)",
|
|
1114
|
+
padding: "1rem",
|
|
1115
|
+
zIndex: 9999,
|
|
1116
|
+
boxShadow: isBottom ? "0 -4px 6px -1px rgb(0 0 0 / 0.1)" : "0 4px 6px -1px rgb(0 0 0 / 0.1)"
|
|
1117
|
+
},
|
|
1118
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1119
|
+
"div",
|
|
1120
|
+
{
|
|
1121
|
+
style: {
|
|
1122
|
+
maxWidth: "1200px",
|
|
1123
|
+
margin: "0 auto",
|
|
1124
|
+
display: "flex",
|
|
1125
|
+
alignItems: "center",
|
|
1126
|
+
justifyContent: "space-between",
|
|
1127
|
+
gap: "1rem",
|
|
1128
|
+
flexWrap: "wrap"
|
|
1129
|
+
},
|
|
1130
|
+
children: [
|
|
1131
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, minWidth: "200px" }, children: [
|
|
1132
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { id: "cookie-consent-title", style: { fontSize: "1rem", fontWeight: "600", color: "white", marginBottom: "0.25rem" }, children: "We use cookies" }),
|
|
1133
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { id: "cookie-consent-description", style: { fontSize: "0.875rem", color: "#d1d5db", margin: 0 }, children: [
|
|
1134
|
+
"We use cookies to improve your experience and analyze usage. By continuing, you agree to our",
|
|
1135
|
+
" ",
|
|
1136
|
+
privacyPolicyUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1137
|
+
"a",
|
|
1138
|
+
{
|
|
1139
|
+
href: privacyPolicyUrl,
|
|
1140
|
+
style: { color: "#60a5fa", textDecoration: "underline" },
|
|
1141
|
+
children: "Privacy Policy"
|
|
1142
|
+
}
|
|
1143
|
+
)
|
|
1144
|
+
] })
|
|
1145
|
+
] }),
|
|
1146
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "0.5rem", flexWrap: "wrap" }, children: [
|
|
1147
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1148
|
+
"button",
|
|
1149
|
+
{
|
|
1150
|
+
onClick: handleReject,
|
|
1151
|
+
style: {
|
|
1152
|
+
padding: "0.5rem 1rem",
|
|
1153
|
+
backgroundColor: "transparent",
|
|
1154
|
+
border: "1px solid #4b5563",
|
|
1155
|
+
borderRadius: "0.375rem",
|
|
1156
|
+
color: "#d1d5db",
|
|
1157
|
+
fontSize: "0.875rem",
|
|
1158
|
+
fontWeight: "500",
|
|
1159
|
+
cursor: "pointer",
|
|
1160
|
+
transition: "all 0.15s ease"
|
|
1161
|
+
},
|
|
1162
|
+
onMouseEnter: (e) => {
|
|
1163
|
+
e.currentTarget.style.backgroundColor = "#374151";
|
|
1164
|
+
},
|
|
1165
|
+
onMouseLeave: (e) => {
|
|
1166
|
+
e.currentTarget.style.backgroundColor = "transparent";
|
|
1167
|
+
},
|
|
1168
|
+
children: rejectAllText
|
|
1169
|
+
}
|
|
1170
|
+
),
|
|
1171
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1172
|
+
"button",
|
|
1173
|
+
{
|
|
1174
|
+
onClick: handleCustomize,
|
|
1175
|
+
style: {
|
|
1176
|
+
padding: "0.5rem 1rem",
|
|
1177
|
+
backgroundColor: "transparent",
|
|
1178
|
+
border: "1px solid #4b5563",
|
|
1179
|
+
borderRadius: "0.375rem",
|
|
1180
|
+
color: "#d1d5db",
|
|
1181
|
+
fontSize: "0.875rem",
|
|
1182
|
+
fontWeight: "500",
|
|
1183
|
+
cursor: "pointer",
|
|
1184
|
+
transition: "all 0.15s ease"
|
|
1185
|
+
},
|
|
1186
|
+
onMouseEnter: (e) => {
|
|
1187
|
+
e.currentTarget.style.backgroundColor = "#374151";
|
|
1188
|
+
},
|
|
1189
|
+
onMouseLeave: (e) => {
|
|
1190
|
+
e.currentTarget.style.backgroundColor = "transparent";
|
|
1191
|
+
},
|
|
1192
|
+
children: customizeText
|
|
1193
|
+
}
|
|
1194
|
+
),
|
|
1195
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1196
|
+
"button",
|
|
1197
|
+
{
|
|
1198
|
+
onClick: handleAccept,
|
|
1199
|
+
style: {
|
|
1200
|
+
padding: "0.5rem 1rem",
|
|
1201
|
+
backgroundColor: "#3b82f6",
|
|
1202
|
+
border: "none",
|
|
1203
|
+
borderRadius: "0.375rem",
|
|
1204
|
+
color: "white",
|
|
1205
|
+
fontSize: "0.875rem",
|
|
1206
|
+
fontWeight: "500",
|
|
1207
|
+
cursor: "pointer",
|
|
1208
|
+
transition: "all 0.15s ease"
|
|
1209
|
+
},
|
|
1210
|
+
onMouseEnter: (e) => {
|
|
1211
|
+
e.currentTarget.style.backgroundColor = "#2563eb";
|
|
1212
|
+
},
|
|
1213
|
+
onMouseLeave: (e) => {
|
|
1214
|
+
e.currentTarget.style.backgroundColor = "#3b82f6";
|
|
1215
|
+
},
|
|
1216
|
+
children: acceptAllText
|
|
1217
|
+
}
|
|
1218
|
+
)
|
|
1219
|
+
] })
|
|
1220
|
+
]
|
|
1221
|
+
}
|
|
1222
|
+
)
|
|
1223
|
+
}
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1226
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1227
|
+
showCustomize && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1228
|
+
"div",
|
|
1229
|
+
{
|
|
1230
|
+
onClick: handleCloseModal,
|
|
1231
|
+
style: {
|
|
1232
|
+
position: "fixed",
|
|
1233
|
+
inset: 0,
|
|
1234
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
1235
|
+
zIndex: 9998
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
),
|
|
1239
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1240
|
+
"div",
|
|
1241
|
+
{
|
|
1242
|
+
className,
|
|
1243
|
+
role: "dialog",
|
|
1244
|
+
"aria-labelledby": "cookie-consent-title",
|
|
1245
|
+
"aria-modal": "true",
|
|
1246
|
+
style: {
|
|
1247
|
+
position: "fixed",
|
|
1248
|
+
left: "50%",
|
|
1249
|
+
top: "50%",
|
|
1250
|
+
transform: "translate(-50%, -50%)",
|
|
1251
|
+
backgroundColor: "white",
|
|
1252
|
+
borderRadius: "0.75rem",
|
|
1253
|
+
padding: "1.5rem",
|
|
1254
|
+
zIndex: 9999,
|
|
1255
|
+
maxWidth: "500px",
|
|
1256
|
+
width: "90%",
|
|
1257
|
+
maxHeight: "80vh",
|
|
1258
|
+
overflowY: "auto",
|
|
1259
|
+
boxShadow: "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)"
|
|
1260
|
+
},
|
|
1261
|
+
children: [
|
|
1262
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1263
|
+
"h2",
|
|
1264
|
+
{
|
|
1265
|
+
id: "cookie-consent-title",
|
|
1266
|
+
style: { fontSize: "1.25rem", fontWeight: "700", color: "#111827", marginBottom: "0.5rem" },
|
|
1267
|
+
children: "Customize Cookie Preferences"
|
|
1268
|
+
}
|
|
1269
|
+
),
|
|
1270
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.875rem", color: "#6b7280", marginBottom: "1.5rem" }, children: "We use different types of cookies to provide you with the best experience. You can choose which cookies to accept." }),
|
|
1271
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: "1.5rem" }, children: categories.map((category) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1272
|
+
"div",
|
|
1273
|
+
{
|
|
1274
|
+
style: {
|
|
1275
|
+
display: "flex",
|
|
1276
|
+
justifyContent: "space-between",
|
|
1277
|
+
alignItems: "flex-start",
|
|
1278
|
+
padding: "0.75rem 0",
|
|
1279
|
+
borderBottom: "1px solid #e5e7eb"
|
|
1280
|
+
},
|
|
1281
|
+
children: [
|
|
1282
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1 }, children: [
|
|
1283
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
1284
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { style: { fontSize: "0.875rem", fontWeight: "600", color: "#111827", margin: 0 }, children: category.name }),
|
|
1285
|
+
category.required && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1286
|
+
"span",
|
|
1287
|
+
{
|
|
1288
|
+
style: {
|
|
1289
|
+
fontSize: "0.75rem",
|
|
1290
|
+
backgroundColor: "#dbeafe",
|
|
1291
|
+
color: "#1e40af",
|
|
1292
|
+
padding: "0.125rem 0.375rem",
|
|
1293
|
+
borderRadius: "0.25rem",
|
|
1294
|
+
fontWeight: "500"
|
|
1295
|
+
},
|
|
1296
|
+
children: "Required"
|
|
1297
|
+
}
|
|
1298
|
+
)
|
|
1299
|
+
] }),
|
|
1300
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.875rem", color: "#6b7280", margin: "0.25rem 0 0", marginTop: "0.25rem" }, children: category.description })
|
|
1301
|
+
] }),
|
|
1302
|
+
!category.required && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1303
|
+
"button",
|
|
1304
|
+
{
|
|
1305
|
+
onClick: () => toggleCategory(category.id),
|
|
1306
|
+
style: {
|
|
1307
|
+
position: "relative",
|
|
1308
|
+
width: "44px",
|
|
1309
|
+
height: "24px",
|
|
1310
|
+
backgroundColor: selectedCategories[category.id] ? "#3b82f6" : "#d1d5db",
|
|
1311
|
+
borderRadius: "12px",
|
|
1312
|
+
border: "none",
|
|
1313
|
+
cursor: "pointer",
|
|
1314
|
+
transition: "background-color 0.15s ease"
|
|
1315
|
+
},
|
|
1316
|
+
"aria-pressed": selectedCategories[category.id],
|
|
1317
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1318
|
+
"span",
|
|
1319
|
+
{
|
|
1320
|
+
style: {
|
|
1321
|
+
position: "absolute",
|
|
1322
|
+
top: "2px",
|
|
1323
|
+
left: selectedCategories[category.id] ? "22px" : "2px",
|
|
1324
|
+
width: "20px",
|
|
1325
|
+
height: "20px",
|
|
1326
|
+
backgroundColor: "white",
|
|
1327
|
+
borderRadius: "50%",
|
|
1328
|
+
transition: "left 0.15s ease"
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
)
|
|
1332
|
+
}
|
|
1333
|
+
)
|
|
1334
|
+
]
|
|
1335
|
+
},
|
|
1336
|
+
category.id
|
|
1337
|
+
)) }),
|
|
1338
|
+
privacyPolicyUrl && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { fontSize: "0.875rem", color: "#6b7280", marginBottom: "1rem" }, children: [
|
|
1339
|
+
"Read more in our",
|
|
1340
|
+
" ",
|
|
1341
|
+
/* @__PURE__ */ jsxRuntime.jsx("a", { href: privacyPolicyUrl, style: { color: "#3b82f6", textDecoration: "underline" }, children: "Privacy Policy" })
|
|
1342
|
+
] }),
|
|
1343
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "0.75rem", justifyContent: "flex-end" }, children: [
|
|
1344
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1345
|
+
"button",
|
|
1346
|
+
{
|
|
1347
|
+
onClick: handleCloseModal,
|
|
1348
|
+
style: {
|
|
1349
|
+
padding: "0.5rem 1rem",
|
|
1350
|
+
backgroundColor: "white",
|
|
1351
|
+
border: "1px solid #d1d5db",
|
|
1352
|
+
borderRadius: "0.375rem",
|
|
1353
|
+
color: "#374151",
|
|
1354
|
+
fontSize: "0.875rem",
|
|
1355
|
+
fontWeight: "500",
|
|
1356
|
+
cursor: "pointer",
|
|
1357
|
+
transition: "all 0.15s ease"
|
|
1358
|
+
},
|
|
1359
|
+
onMouseEnter: (e) => {
|
|
1360
|
+
e.currentTarget.style.backgroundColor = "#f9fafb";
|
|
1361
|
+
},
|
|
1362
|
+
onMouseLeave: (e) => {
|
|
1363
|
+
e.currentTarget.style.backgroundColor = "white";
|
|
1364
|
+
},
|
|
1365
|
+
children: "Cancel"
|
|
1366
|
+
}
|
|
1367
|
+
),
|
|
1368
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1369
|
+
"button",
|
|
1370
|
+
{
|
|
1371
|
+
onClick: handleSaveCustom,
|
|
1372
|
+
style: {
|
|
1373
|
+
padding: "0.5rem 1rem",
|
|
1374
|
+
backgroundColor: "#3b82f6",
|
|
1375
|
+
border: "none",
|
|
1376
|
+
borderRadius: "0.375rem",
|
|
1377
|
+
color: "white",
|
|
1378
|
+
fontSize: "0.875rem",
|
|
1379
|
+
fontWeight: "500",
|
|
1380
|
+
cursor: "pointer",
|
|
1381
|
+
transition: "all 0.15s ease"
|
|
1382
|
+
},
|
|
1383
|
+
onMouseEnter: (e) => {
|
|
1384
|
+
e.currentTarget.style.backgroundColor = "#2563eb";
|
|
1385
|
+
},
|
|
1386
|
+
onMouseLeave: (e) => {
|
|
1387
|
+
e.currentTarget.style.backgroundColor = "#3b82f6";
|
|
1388
|
+
},
|
|
1389
|
+
children: "Save Preferences"
|
|
1390
|
+
}
|
|
1391
|
+
)
|
|
1392
|
+
] })
|
|
1393
|
+
]
|
|
1394
|
+
}
|
|
1395
|
+
)
|
|
1396
|
+
] });
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
// src/accessibility/validators.ts
|
|
1400
|
+
function validateAriaLabel(ariaLabel, ariaLabelledby, elementType = "button") {
|
|
1401
|
+
const issues = [];
|
|
1402
|
+
const elementsRequiringLabel = ["button", "a", "input", "textarea", "select"];
|
|
1403
|
+
if (elementsRequiringLabel.includes(elementType)) {
|
|
1404
|
+
if (!ariaLabel && !ariaLabelledby) {
|
|
1405
|
+
issues.push({
|
|
1406
|
+
severity: "error",
|
|
1407
|
+
message: `${elementType} must have an accessible name via aria-label or aria-labelledby`,
|
|
1408
|
+
attribute: "aria-label"
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
if (ariaLabel === "") {
|
|
1413
|
+
issues.push({
|
|
1414
|
+
severity: "error",
|
|
1415
|
+
message: "aria-label cannot be empty",
|
|
1416
|
+
attribute: "aria-label"
|
|
1417
|
+
});
|
|
1418
|
+
}
|
|
1419
|
+
if (ariaLabel && ariaLabelledby) {
|
|
1420
|
+
issues.push({
|
|
1421
|
+
severity: "warning",
|
|
1422
|
+
message: "Use either aria-label or aria-labelledby, not both",
|
|
1423
|
+
attribute: "aria-label"
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
return {
|
|
1427
|
+
valid: issues.filter((i) => i.severity === "error").length === 0,
|
|
1428
|
+
element: elementType,
|
|
1429
|
+
issues
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
var variantStyles = {
|
|
1433
|
+
default: "bg-blue-600 text-white hover:bg-blue-700",
|
|
1434
|
+
destructive: "bg-red-600 text-white hover:bg-red-700",
|
|
1435
|
+
outline: "border-2 border-gray-300 text-gray-700 hover:bg-gray-50",
|
|
1436
|
+
secondary: "bg-gray-200 text-gray-900 hover:bg-gray-300",
|
|
1437
|
+
ghost: "text-gray-700 hover:bg-gray-100",
|
|
1438
|
+
link: "text-blue-600 underline hover:text-blue-700"
|
|
1439
|
+
};
|
|
1440
|
+
var sizeStyles = {
|
|
1441
|
+
default: "px-4 py-2 text-sm",
|
|
1442
|
+
sm: "px-3 py-1 text-xs",
|
|
1443
|
+
lg: "px-6 py-3 text-base",
|
|
1444
|
+
icon: "p-2"
|
|
1445
|
+
};
|
|
1446
|
+
var VocoButton = react.forwardRef(
|
|
1447
|
+
({
|
|
1448
|
+
children,
|
|
1449
|
+
variant = "default",
|
|
1450
|
+
size = "default",
|
|
1451
|
+
disabled = false,
|
|
1452
|
+
type = "button",
|
|
1453
|
+
"aria-label": ariaLabel,
|
|
1454
|
+
"aria-labelledby": ariaLabelledby,
|
|
1455
|
+
"aria-describedby": ariaDescribedby,
|
|
1456
|
+
role,
|
|
1457
|
+
className = "",
|
|
1458
|
+
...props
|
|
1459
|
+
}, ref) => {
|
|
1460
|
+
if (process.env.NODE_ENV === "development") {
|
|
1461
|
+
const validation = validateAriaLabel(ariaLabel, ariaLabelledby, "button");
|
|
1462
|
+
if (!validation.valid && !children) {
|
|
1463
|
+
console.warn("VocoButton accessibility warning:", validation.issues);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
const baseStyles = "inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed";
|
|
1467
|
+
const hasAccessibleName = children || ariaLabel || ariaLabelledby;
|
|
1468
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1469
|
+
"button",
|
|
1470
|
+
{
|
|
1471
|
+
ref,
|
|
1472
|
+
type,
|
|
1473
|
+
disabled,
|
|
1474
|
+
"aria-label": ariaLabel,
|
|
1475
|
+
"aria-labelledby": ariaLabelledby,
|
|
1476
|
+
"aria-describedby": ariaDescribedby,
|
|
1477
|
+
role,
|
|
1478
|
+
className: `${baseStyles} ${variantStyles[variant]} ${sizeStyles[size]} ${className}`.trim(),
|
|
1479
|
+
...hasAccessibleName ? {} : { "aria-label": "Button" },
|
|
1480
|
+
...props,
|
|
1481
|
+
children
|
|
1482
|
+
}
|
|
1483
|
+
);
|
|
1484
|
+
}
|
|
1485
|
+
);
|
|
1486
|
+
VocoButton.displayName = "VocoButton";
|
|
1487
|
+
var VocoInput = react.forwardRef(
|
|
1488
|
+
({
|
|
1489
|
+
id,
|
|
1490
|
+
label,
|
|
1491
|
+
error,
|
|
1492
|
+
hint,
|
|
1493
|
+
required = false,
|
|
1494
|
+
type = "text",
|
|
1495
|
+
disabled = false,
|
|
1496
|
+
placeholder,
|
|
1497
|
+
"aria-label": ariaLabel,
|
|
1498
|
+
"aria-labelledby": ariaLabelledby,
|
|
1499
|
+
"aria-describedby": ariaDescribedby,
|
|
1500
|
+
role,
|
|
1501
|
+
className = "",
|
|
1502
|
+
...props
|
|
1503
|
+
}, ref) => {
|
|
1504
|
+
const inputId = id || `input-${Math.random().toString(36).substring(2, 9)}`;
|
|
1505
|
+
const errorId = error ? `${inputId}-error` : void 0;
|
|
1506
|
+
const hintId = hint ? `${inputId}-hint` : void 0;
|
|
1507
|
+
const describedByParts = [
|
|
1508
|
+
ariaDescribedby,
|
|
1509
|
+
error ? errorId : void 0,
|
|
1510
|
+
hint ? hintId : void 0
|
|
1511
|
+
].filter(Boolean).join(" ");
|
|
1512
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
1513
|
+
label && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1514
|
+
"label",
|
|
1515
|
+
{
|
|
1516
|
+
htmlFor: inputId,
|
|
1517
|
+
className: "text-sm font-medium text-gray-700",
|
|
1518
|
+
id: ariaLabelledby,
|
|
1519
|
+
children: [
|
|
1520
|
+
label,
|
|
1521
|
+
required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500 ml-1", "aria-label": "required", children: "*" })
|
|
1522
|
+
]
|
|
1523
|
+
}
|
|
1524
|
+
),
|
|
1525
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1526
|
+
"input",
|
|
1527
|
+
{
|
|
1528
|
+
ref,
|
|
1529
|
+
id: inputId,
|
|
1530
|
+
type,
|
|
1531
|
+
disabled,
|
|
1532
|
+
placeholder,
|
|
1533
|
+
"aria-label": ariaLabel,
|
|
1534
|
+
"aria-labelledby": ariaLabelledby,
|
|
1535
|
+
"aria-describedby": describedByParts || void 0,
|
|
1536
|
+
"aria-invalid": !!error,
|
|
1537
|
+
"aria-required": required,
|
|
1538
|
+
required,
|
|
1539
|
+
role,
|
|
1540
|
+
className: `
|
|
1541
|
+
px-3 py-2 rounded-md border transition-colors
|
|
1542
|
+
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
|
|
1543
|
+
disabled:opacity-50 disabled:cursor-not-allowed
|
|
1544
|
+
${error ? "border-red-500 focus:border-red-500 focus:ring-red-500" : "border-gray-300 focus:border-blue-500"}
|
|
1545
|
+
${className}
|
|
1546
|
+
`.trim().replace(/\s+/g, " "),
|
|
1547
|
+
...props
|
|
1548
|
+
}
|
|
1549
|
+
),
|
|
1550
|
+
hint && !error && /* @__PURE__ */ jsxRuntime.jsx("span", { id: hintId, className: "text-xs text-gray-500", children: hint }),
|
|
1551
|
+
error && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1552
|
+
"span",
|
|
1553
|
+
{
|
|
1554
|
+
id: errorId,
|
|
1555
|
+
className: "text-xs text-red-500",
|
|
1556
|
+
role: "alert",
|
|
1557
|
+
"aria-live": "polite",
|
|
1558
|
+
children: error
|
|
1559
|
+
}
|
|
1560
|
+
)
|
|
1561
|
+
] });
|
|
1562
|
+
}
|
|
1563
|
+
);
|
|
1564
|
+
VocoInput.displayName = "VocoInput";
|
|
1565
|
+
var VocoForm = react.forwardRef(
|
|
1566
|
+
({
|
|
1567
|
+
children,
|
|
1568
|
+
onSubmit,
|
|
1569
|
+
"aria-live": ariaLive = "polite",
|
|
1570
|
+
"aria-label": ariaLabel,
|
|
1571
|
+
"aria-labelledby": ariaLabelledby,
|
|
1572
|
+
"aria-describedby": ariaDescribedby,
|
|
1573
|
+
role,
|
|
1574
|
+
className = "",
|
|
1575
|
+
...props
|
|
1576
|
+
}, ref) => {
|
|
1577
|
+
const [errors, setErrors] = react.useState({});
|
|
1578
|
+
const [isSubmitting, setIsSubmitting] = react.useState(false);
|
|
1579
|
+
const handleSubmit = react.useCallback(
|
|
1580
|
+
async (event) => {
|
|
1581
|
+
event.preventDefault();
|
|
1582
|
+
setIsSubmitting(true);
|
|
1583
|
+
const newErrors = {};
|
|
1584
|
+
const requiredElements = event.currentTarget.querySelectorAll("[required]");
|
|
1585
|
+
requiredElements.forEach((element) => {
|
|
1586
|
+
const input = element;
|
|
1587
|
+
if (!input.value.trim()) {
|
|
1588
|
+
const fieldName = input.getAttribute("aria-label") || input.getAttribute("name") || "Field";
|
|
1589
|
+
newErrors[input.name || input.id || Math.random().toString()] = `${fieldName} is required`;
|
|
1590
|
+
}
|
|
1591
|
+
});
|
|
1592
|
+
setErrors(newErrors);
|
|
1593
|
+
if (Object.keys(newErrors).length > 0) {
|
|
1594
|
+
setIsSubmitting(false);
|
|
1595
|
+
const firstErrorInput = event.currentTarget.querySelector('[aria-invalid="true"]');
|
|
1596
|
+
firstErrorInput?.focus();
|
|
1597
|
+
return;
|
|
1598
|
+
}
|
|
1599
|
+
try {
|
|
1600
|
+
await onSubmit?.(event);
|
|
1601
|
+
} finally {
|
|
1602
|
+
setIsSubmitting(false);
|
|
1603
|
+
}
|
|
1604
|
+
},
|
|
1605
|
+
[onSubmit]
|
|
1606
|
+
);
|
|
1607
|
+
const formStatusId = `form-status-${Math.random().toString(36).substring(2, 9)}`;
|
|
1608
|
+
const hasErrors = Object.keys(errors).length > 0;
|
|
1609
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1610
|
+
"form",
|
|
1611
|
+
{
|
|
1612
|
+
ref,
|
|
1613
|
+
onSubmit: handleSubmit,
|
|
1614
|
+
"aria-live": ariaLive,
|
|
1615
|
+
"aria-label": ariaLabel,
|
|
1616
|
+
"aria-labelledby": ariaLabelledby,
|
|
1617
|
+
"aria-describedby": ariaDescribedby,
|
|
1618
|
+
role,
|
|
1619
|
+
"aria-busy": isSubmitting,
|
|
1620
|
+
noValidate: true,
|
|
1621
|
+
className,
|
|
1622
|
+
...props,
|
|
1623
|
+
children: [
|
|
1624
|
+
children,
|
|
1625
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1626
|
+
"div",
|
|
1627
|
+
{
|
|
1628
|
+
id: formStatusId,
|
|
1629
|
+
role: "status",
|
|
1630
|
+
"aria-live": "polite",
|
|
1631
|
+
className: "sr-only",
|
|
1632
|
+
"aria-atomic": "true",
|
|
1633
|
+
children: [
|
|
1634
|
+
isSubmitting && "Submitting form...",
|
|
1635
|
+
hasErrors && "Form has errors. Please correct them and try again."
|
|
1636
|
+
]
|
|
1637
|
+
}
|
|
1638
|
+
)
|
|
1639
|
+
]
|
|
1640
|
+
}
|
|
1641
|
+
);
|
|
1642
|
+
}
|
|
1643
|
+
);
|
|
1644
|
+
VocoForm.displayName = "VocoForm";
|
|
1645
|
+
function VocoAuditLog({
|
|
1646
|
+
userId: _userId,
|
|
1647
|
+
filters = {},
|
|
1648
|
+
limit = 50,
|
|
1649
|
+
className = "",
|
|
1650
|
+
onExport,
|
|
1651
|
+
onLogClick,
|
|
1652
|
+
showFilters = true,
|
|
1653
|
+
showExport = true
|
|
1654
|
+
}) {
|
|
1655
|
+
const [logs, setLogs] = react.useState([]);
|
|
1656
|
+
const [total, setTotal] = react.useState(0);
|
|
1657
|
+
const [loading, setLoading] = react.useState(false);
|
|
1658
|
+
const [hasMore, setHasMore] = react.useState(false);
|
|
1659
|
+
const [offset, setOffset] = react.useState(0);
|
|
1660
|
+
const [currentFilters, setCurrentFilters] = react.useState(filters);
|
|
1661
|
+
react.useEffect(() => {
|
|
1662
|
+
fetchLogs();
|
|
1663
|
+
}, [currentFilters, offset]);
|
|
1664
|
+
async function fetchLogs() {
|
|
1665
|
+
setLoading(true);
|
|
1666
|
+
try {
|
|
1667
|
+
const result = {
|
|
1668
|
+
logs: [],
|
|
1669
|
+
total: 0,
|
|
1670
|
+
hasMore: false
|
|
1671
|
+
};
|
|
1672
|
+
setLogs(result.logs);
|
|
1673
|
+
setTotal(result.total);
|
|
1674
|
+
setHasMore(result.hasMore);
|
|
1675
|
+
} catch (error) {
|
|
1676
|
+
console.error("Failed to fetch audit logs:", error);
|
|
1677
|
+
} finally {
|
|
1678
|
+
setLoading(false);
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
function handleFilterChange(key, value) {
|
|
1682
|
+
setCurrentFilters({ ...currentFilters, [key]: value });
|
|
1683
|
+
setOffset(0);
|
|
1684
|
+
}
|
|
1685
|
+
function handleLoadMore() {
|
|
1686
|
+
setOffset(offset + limit);
|
|
1687
|
+
}
|
|
1688
|
+
function handleExportClick(format) {
|
|
1689
|
+
onExport?.(format);
|
|
1690
|
+
}
|
|
1691
|
+
function getActionColor(action) {
|
|
1692
|
+
switch (action) {
|
|
1693
|
+
case "INSERT":
|
|
1694
|
+
return "text-green-600 bg-green-50";
|
|
1695
|
+
case "UPDATE":
|
|
1696
|
+
return "text-blue-600 bg-blue-50";
|
|
1697
|
+
case "DELETE":
|
|
1698
|
+
return "text-red-600 bg-red-50";
|
|
1699
|
+
default:
|
|
1700
|
+
return "text-gray-600 bg-gray-50";
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
|
|
1704
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1705
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-bold text-gray-900", children: "Audit Log" }),
|
|
1706
|
+
showExport && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
1707
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1708
|
+
"button",
|
|
1709
|
+
{
|
|
1710
|
+
onClick: () => handleExportClick("csv"),
|
|
1711
|
+
className: "px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700",
|
|
1712
|
+
children: "Export CSV"
|
|
1713
|
+
}
|
|
1714
|
+
),
|
|
1715
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1716
|
+
"button",
|
|
1717
|
+
{
|
|
1718
|
+
onClick: () => handleExportClick("json"),
|
|
1719
|
+
className: "px-4 py-2 bg-gray-600 text-white rounded-md hover:bg-gray-700",
|
|
1720
|
+
children: "Export JSON"
|
|
1721
|
+
}
|
|
1722
|
+
)
|
|
1723
|
+
] })
|
|
1724
|
+
] }),
|
|
1725
|
+
showFilters && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 bg-gray-50 rounded-md space-y-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-4 gap-4", children: [
|
|
1726
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1727
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: "Action" }),
|
|
1728
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1729
|
+
"select",
|
|
1730
|
+
{
|
|
1731
|
+
value: currentFilters.action || "",
|
|
1732
|
+
onChange: (e) => handleFilterChange("action", e.target.value),
|
|
1733
|
+
className: "w-full px-3 py-2 border rounded-md",
|
|
1734
|
+
children: [
|
|
1735
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "All Actions" }),
|
|
1736
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "INSERT", children: "Insert" }),
|
|
1737
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "UPDATE", children: "Update" }),
|
|
1738
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "DELETE", children: "Delete" }),
|
|
1739
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "LOGIN", children: "Login" }),
|
|
1740
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "LOGOUT", children: "Logout" })
|
|
1741
|
+
]
|
|
1742
|
+
}
|
|
1743
|
+
)
|
|
1744
|
+
] }),
|
|
1745
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1746
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: "Table" }),
|
|
1747
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1748
|
+
"input",
|
|
1749
|
+
{
|
|
1750
|
+
type: "text",
|
|
1751
|
+
value: currentFilters.table || "",
|
|
1752
|
+
onChange: (e) => handleFilterChange("table", e.target.value),
|
|
1753
|
+
placeholder: "e.g. users",
|
|
1754
|
+
className: "w-full px-3 py-2 border rounded-md"
|
|
1755
|
+
}
|
|
1756
|
+
)
|
|
1757
|
+
] }),
|
|
1758
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1759
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: "Start Date" }),
|
|
1760
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1761
|
+
"input",
|
|
1762
|
+
{
|
|
1763
|
+
type: "date",
|
|
1764
|
+
value: currentFilters.startDate ? currentFilters.startDate.toISOString().split("T")[0] : "",
|
|
1765
|
+
onChange: (e) => handleFilterChange("startDate", e.target.value ? new Date(e.target.value) : void 0),
|
|
1766
|
+
className: "w-full px-3 py-2 border rounded-md"
|
|
1767
|
+
}
|
|
1768
|
+
)
|
|
1769
|
+
] }),
|
|
1770
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1771
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: "End Date" }),
|
|
1772
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1773
|
+
"input",
|
|
1774
|
+
{
|
|
1775
|
+
type: "date",
|
|
1776
|
+
value: currentFilters.endDate ? currentFilters.endDate.toISOString().split("T")[0] : "",
|
|
1777
|
+
onChange: (e) => handleFilterChange("endDate", e.target.value ? new Date(e.target.value) : void 0),
|
|
1778
|
+
className: "w-full px-3 py-2 border rounded-md"
|
|
1779
|
+
}
|
|
1780
|
+
)
|
|
1781
|
+
] })
|
|
1782
|
+
] }) }),
|
|
1783
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-3 gap-4", children: [
|
|
1784
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 bg-white rounded-md border", children: [
|
|
1785
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-gray-500", children: "Total Entries" }),
|
|
1786
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-2xl font-bold", children: total })
|
|
1787
|
+
] }),
|
|
1788
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 bg-white rounded-md border", children: [
|
|
1789
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-gray-500", children: "Showing" }),
|
|
1790
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-2xl font-bold", children: logs.length })
|
|
1791
|
+
] }),
|
|
1792
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 bg-white rounded-md border", children: [
|
|
1793
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-gray-500", children: "Offset" }),
|
|
1794
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-2xl font-bold", children: offset })
|
|
1795
|
+
] })
|
|
1796
|
+
] }),
|
|
1797
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "min-w-full divide-y divide-gray-200", children: [
|
|
1798
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-gray-50", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
1799
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase", children: "Timestamp" }),
|
|
1800
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase", children: "Action" }),
|
|
1801
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase", children: "Table" }),
|
|
1802
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase", children: "Record ID" }),
|
|
1803
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase", children: "Changes" })
|
|
1804
|
+
] }) }),
|
|
1805
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "bg-white divide-y divide-gray-200", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx("td", { colSpan: 5, className: "px-6 py-4 text-center text-gray-500", children: "Loading..." }) }) : logs.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx("td", { colSpan: 5, className: "px-6 py-4 text-center text-gray-500", children: "No audit logs found" }) }) : logs.map((log) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1806
|
+
"tr",
|
|
1807
|
+
{
|
|
1808
|
+
onClick: () => onLogClick?.(log.id),
|
|
1809
|
+
className: onLogClick ? "cursor-pointer hover:bg-gray-50" : "",
|
|
1810
|
+
children: [
|
|
1811
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-6 py-4 whitespace-nowrap text-sm text-gray-900", children: log.timestamp.toLocaleString() }),
|
|
1812
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-6 py-4 whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1813
|
+
"span",
|
|
1814
|
+
{
|
|
1815
|
+
className: `px-2 py-1 text-xs font-medium rounded-full ${getActionColor(log.action)}`,
|
|
1816
|
+
children: log.action
|
|
1817
|
+
}
|
|
1818
|
+
) }),
|
|
1819
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-6 py-4 whitespace-nowrap text-sm text-gray-900", children: log.tableName }),
|
|
1820
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-6 py-4 whitespace-nowrap text-sm text-gray-900", children: log.recordId }),
|
|
1821
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-6 py-4 text-sm text-gray-500", children: /* @__PURE__ */ jsxRuntime.jsxs("details", { children: [
|
|
1822
|
+
/* @__PURE__ */ jsxRuntime.jsxs("summary", { className: "cursor-pointer", children: [
|
|
1823
|
+
Object.keys(log.oldValue || log.newValue || {}).length,
|
|
1824
|
+
" fields"
|
|
1825
|
+
] }),
|
|
1826
|
+
/* @__PURE__ */ jsxRuntime.jsx("pre", { className: "mt-2 text-xs", children: JSON.stringify({ old: log.oldValue, new: log.newValue }, null, 2) })
|
|
1827
|
+
] }) })
|
|
1828
|
+
]
|
|
1829
|
+
},
|
|
1830
|
+
log.id
|
|
1831
|
+
)) })
|
|
1832
|
+
] }) }),
|
|
1833
|
+
hasMore && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center mt-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1834
|
+
"button",
|
|
1835
|
+
{
|
|
1836
|
+
onClick: handleLoadMore,
|
|
1837
|
+
disabled: loading,
|
|
1838
|
+
className: "px-6 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50",
|
|
1839
|
+
children: "Load More"
|
|
1840
|
+
}
|
|
1841
|
+
) })
|
|
1842
|
+
] }) });
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
exports.CookieConsent = CookieConsent;
|
|
1846
|
+
exports.PrivacyPolicy = PrivacyPolicy;
|
|
1847
|
+
exports.TermsOfService = TermsOfService;
|
|
1848
|
+
exports.VocoAuditLog = VocoAuditLog;
|
|
1849
|
+
exports.VocoAuth = VocoAuth;
|
|
1850
|
+
exports.VocoButton = VocoButton;
|
|
1851
|
+
exports.VocoForm = VocoForm;
|
|
1852
|
+
exports.VocoInput = VocoInput;
|
|
1853
|
+
//# sourceMappingURL=react.js.map
|
|
1854
|
+
//# sourceMappingURL=react.js.map
|