diff --git a/website/components/InputGroup.tsx b/website/components/InputGroup.tsx index 8334a26c9..1c967f1b7 100644 --- a/website/components/InputGroup.tsx +++ b/website/components/InputGroup.tsx @@ -1,7 +1,7 @@ import { createMemo, createSignal, type JSX, useContext } from "solid-js"; import { isServer } from "solid-js/web"; import { StoreContext } from "#components/Store"; -import { track } from "../lib/track"; +import { formatFeatureFlags, track } from "../lib/track"; import { Cli } from "./Cli"; import Stackblitz from "./Stackblitz"; @@ -28,7 +28,7 @@ export default function InputGroup() { const [tooltipText, setTooltipText] = createSignal("Copy to Clipboard"); function getFlags() { - return selectedFeaturesFlags().map((flag) => `--${flag}`); + return selectedFeaturesFlags().map(({ flag }) => `--${flag}`); } const npm = createMemo(() => ["npm", "create", "vike@latest", "---", ...getFlags()]); @@ -42,9 +42,10 @@ export default function InputGroup() { }; const handleCopy = (packageManager: string) => { - track("copy", { - flags: selectedFeaturesFlags(), + const selectedFlags = selectedFeaturesFlags(); + track("copy_scaffold", { package_manager: packageManager, + ...formatFeatureFlags(selectedFlags), }); setTooltipText("Copied to Clipboard!"); }; diff --git a/website/components/Store.tsx b/website/components/Store.tsx index 528a52614..29c905778 100644 --- a/website/components/Store.tsx +++ b/website/components/Store.tsx @@ -29,7 +29,10 @@ function initStore() { const selectedFeaturesFlags = createMemo(() => selectedFeatures() .filter((f) => !f.invisibleCli) - .map((f) => f.flag), + .map((f) => ({ + flag: f.flag, + category: f.category.toLowerCase().replaceAll(" ", "_"), + })), ); function selectPreset(ks: (Flags | CategoryLabels)[]) { diff --git a/website/lib/track.ts b/website/lib/track.ts index eede86361..277fd28e7 100644 --- a/website/lib/track.ts +++ b/website/lib/track.ts @@ -1,53 +1,64 @@ -type Events = CopyEvent; +declare global { + // eslint-disable-next-line no-var + var zaraz: Zaraz | undefined; +} -interface CopyEvent { - name: "copy"; - data: { - flags: string[]; - package_manager: string; - }; +/** + * This signature is compatible with both Umami and GA + */ +export function track(name: T["name"], data: T["data"]) { + globalThis.zaraz?.track(name, data); } interface Zaraz { - track: (event: T["name"], data: unknown) => Promise; + track: (event: T["name"], data: unknown) => Promise; } -declare global { - // eslint-disable-next-line no-var - var zaraz: Zaraz | undefined; -} +type TrackingEvent = CopyEvent; -function generateUUID() { - return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { - const r = (Math.random() * 16) | 0, - v = c === "x" ? r : (r & 0x3) | 0x8; - return v.toString(16); - }); +export interface CopyEvent { + name: "copy_scaffold"; + data: { + package_manager: string; + // could be interesting, but maybe tracking + // button clicks as part of a funnel is better + // is_preset: boolean; + [key: string]: string; + }; } -export function track(event: T["name"], data: T["data"]) { - const interactionid = generateUUID(); - console.log({ - interactionid, - event, - data, - }); - switch (event) { - case "copy": { - if (data.flags.length === 0) { - globalThis.zaraz?.track(event, { - flag: undefined, - package_manager: data.package_manager, - interactionid, - }); - } - for (const flag of data.flags) { - globalThis.zaraz?.track(event, { - flag, - package_manager: data.package_manager, - interactionid, - }); +type FeatureFlag = { + flag: string; + category: string; +}; + +export function formatFeatureFlags(selectedFlags: FeatureFlag[]): Record { + const categoriesMultiple = new Set(); + + const flags = selectedFlags.reduce( + (data, { flag, category }) => { + if (data[category]) { + if (Array.isArray(data[category])) { + data[category].push(flag); + } else { + // Track all keys converted to arrays + categoriesMultiple.add(category); + data[category] = [data[category], flag]; + } + } else { + data[category] = flag; } + return data; + }, + {} as Record, + ); + + // Recombine all arrays into strings + categoriesMultiple.forEach((category) => { + if (Array.isArray(flags[category])) { + flags[category] = flags[category].sort().join(":"); } - } + }); + + return flags as Record; }