Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions website/components/InputGroup.tsx
Original file line number Diff line number Diff line change
@@ -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";

Expand All @@ -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()]);
Expand All @@ -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!");
};
Expand Down
5 changes: 4 additions & 1 deletion website/components/Store.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)[]) {
Expand Down
93 changes: 52 additions & 41 deletions website/lib/track.ts
Original file line number Diff line number Diff line change
@@ -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<T extends TrackingEvent>(name: T["name"], data: T["data"]) {
globalThis.zaraz?.track(name, data);
}

interface Zaraz {
track: <T extends Events>(event: T["name"], data: unknown) => Promise<void>;
track: <T extends TrackingEvent>(event: T["name"], data: unknown) => Promise<void>;
}

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<T extends Events>(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<string, string> {
const categoriesMultiple = new Set<string>();

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<string, string | string[]>,
);

// Recombine all arrays into strings
categoriesMultiple.forEach((category) => {
if (Array.isArray(flags[category])) {
flags[category] = flags[category].sort().join(":");
}
}
});

return flags as Record<string, string>;
}