diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/snippet/CookieBannerSnippetPage.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/snippet/CookieBannerSnippetPage.tsx index f084981ae..eb552caa9 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/snippet/CookieBannerSnippetPage.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/snippet/CookieBannerSnippetPage.tsx @@ -16,9 +16,8 @@ import { useTranslate } from "@probo/i18n"; import { Card } from "@probo/ui"; import type { ReactNode } from "react"; -import { ThemePreview } from "#/pages/organizations/cookie-banners/configuration/theme/_components/ThemePreview"; - import { CodeSnippets } from "./_components/CodeSnippets"; +import { ThemePreview } from "./_components/ThemePreview"; export default function CookieBannerSnippetPage() { const { __ } = useTranslate(); diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/snippet/_components/CodeSnippets.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/snippet/_components/CodeSnippets.tsx index 03285e1d2..0d58b248f 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/snippet/_components/CodeSnippets.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/snippet/_components/CodeSnippets.tsx @@ -14,7 +14,6 @@ import { useTranslate } from "@probo/i18n"; import { Button, Card, useToast } from "@probo/ui"; -import { useState } from "react"; import { useParams } from "react-router"; export function CodeSnippets() { @@ -24,52 +23,15 @@ export function CodeSnippets() { const baseUrl = `${window.location.origin}/api/cookie-banner/v1`; - const tabs = [ - { - label: __("Script Tag"), - code: ``, - }, - { - label: __("ES Module"), - code: `import { registerThemedBanner } from "@probo/cookie-banner/themed-banner"; - -registerThemedBanner(); - -// In your HTML or template: -// `, - }, - { - label: __("Headless"), - code: `import { registerComponents } from "@probo/cookie-banner"; - -registerComponents(); - -// Build your own UI with headless components: -// -// -// -// -// -// -// -// `, - }, - ]; - - const [activeTab, setActiveTab] = useState(0); - const activeCode = tabs[activeTab].code; +>`; const handleCopy = () => { - void navigator.clipboard.writeText(activeCode); + void navigator.clipboard.writeText(code); toast({ title: __("Copied"), description: __("Code copied to clipboard"), @@ -78,31 +40,30 @@ registerComponents(); }; return ( - -
-
- {tabs.map((tab, i) => ( - - ))} +
+ +
+
- -
-
-        {activeCode}
-      
- +
+          {code}
+        
+ + +

+ {__("Looking for ES module or headless integration?")} + {" "} + + {__("See our documentation for detailed integration guides.")} + +

+
); } diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/snippet/_components/ThemePreview.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/snippet/_components/ThemePreview.tsx new file mode 100644 index 000000000..19decec3e --- /dev/null +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/snippet/_components/ThemePreview.tsx @@ -0,0 +1,277 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import { useTranslate } from "@probo/i18n"; +import { Button, Card, Field, Input, useToast } from "@probo/ui"; +import { useCallback, useMemo, useState } from "react"; + +type CSSVariable = { + key: string; + label: string; + defaultValue: string; + type: "color" | "text"; +}; + +const CSS_VARIABLES: CSSVariable[] = [ + { key: "--probo-bg", label: "Background", defaultValue: "#ffffff", type: "color" }, + { key: "--probo-text", label: "Text", defaultValue: "#1a1a1a", type: "color" }, + { key: "--probo-text-secondary", label: "Text Secondary", defaultValue: "#555555", type: "color" }, + { key: "--probo-border", label: "Border", defaultValue: "#e0e0e0", type: "color" }, + { key: "--probo-accent", label: "Accent", defaultValue: "#1a1a1a", type: "color" }, + { key: "--probo-accent-text", label: "Accent Text", defaultValue: "#ffffff", type: "color" }, + { key: "--probo-radius", label: "Border Radius", defaultValue: "12px", type: "text" }, + { key: "--probo-btn-radius", label: "Button Radius", defaultValue: "8px", type: "text" }, + { key: "--probo-font-size", label: "Font Size", defaultValue: "14px", type: "text" }, + { key: "--probo-font-family", label: "Font Family", defaultValue: "-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif", type: "text" }, + { key: "--probo-shadow", label: "Shadow", defaultValue: "0 4px 24px rgba(0, 0, 0, 0.12)", type: "text" }, +]; + +function buildCSSSnippet(values: Record): string { + const overrides = CSS_VARIABLES + .filter(v => values[v.key] !== v.defaultValue) + .map(v => ` ${v.key}: ${values[v.key]};`); + + if (overrides.length === 0) { + return "/* Using default theme — no overrides needed */"; + } + + return `probo-cookie-banner {\n${overrides.join("\n")}\n}`; +} + +export function ThemePreview() { + const { __ } = useTranslate(); + const { toast } = useToast(); + + const [values, setValues] = useState>(() => { + const initial: Record = {}; + for (const v of CSS_VARIABLES) { + initial[v.key] = v.defaultValue; + } + return initial; + }); + + const setValue = useCallback((key: string, value: string) => { + setValues(prev => ({ ...prev, [key]: value })); + }, []); + + const handleReset = useCallback(() => { + const initial: Record = {}; + for (const v of CSS_VARIABLES) { + initial[v.key] = v.defaultValue; + } + setValues(initial); + }, []); + + const cssSnippet = useMemo(() => buildCSSSnippet(values), [values]); + + const handleCopyCSS = () => { + void navigator.clipboard.writeText(cssSnippet); + toast({ + title: __("Copied"), + description: __("CSS snippet copied to clipboard"), + variant: "success", + }); + }; + + const previewStyle = useMemo(() => { + const style: Record = {}; + for (const v of CSS_VARIABLES) { + style[v.key] = values[v.key]; + } + return style; + }, [values]); + + const colorVariables = CSS_VARIABLES.filter(v => v.type === "color"); + const textVariables = CSS_VARIABLES.filter(v => v.type === "text"); + + return ( +
+
+

{__("Theme")}

+ +
+ + +
+
+ {colorVariables.map(v => ( +
+ +
+ setValue(v.key, e.target.value)} + className="h-8 w-10 shrink-0 cursor-pointer rounded border border-border-mid bg-transparent p-0.5" + /> + setValue(v.key, e.target.value)} + /> +
+
+ ))} +
+ +
+ {textVariables.map(v => ( + + setValue(v.key, e.target.value)} + /> + + ))} +
+
+
+ +

{__("Preview")}

+ + +
+ +
+
+ +
+

{__("CSS Snippet")}

+ +
+ + +
+          {cssSnippet}
+        
+
+
+ ); +} + +function BannerPreview() { + return ( +
+

+ Cookie Preferences +

+

+ We use cookies to improve your experience and analyze site traffic. + {" "} + e.preventDefault()} + style={{ + color: "var(--probo-accent, #1a1a1a)", + textDecoration: "underline", + }} + > + Privacy Policy + +

+
+ + + +
+
+ ); +}