Add demo banner star-repo CTA analytics and fix CI formatting (#183)

* add star cta

* feat: track demo banner star repo clicks

* chore: format docs-site json for biome ci
This commit is contained in:
Shaheer Sarfaraz 2026-02-18 13:21:32 +00:00 committed by GitHub
parent ddf5130915
commit 16fdb425d8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 134 additions and 16 deletions

View File

@ -4,9 +4,7 @@
{ {
"type": "category", "type": "category",
"label": "Getting Started", "label": "Getting Started",
"items": [ "items": ["getting-started/self-hosting"]
"getting-started/self-hosting"
]
}, },
{ {
"type": "category", "type": "category",
@ -58,17 +56,12 @@
{ {
"type": "category", "type": "category",
"label": "Troubleshooting", "label": "Troubleshooting",
"items": [ "items": ["troubleshooting/common-problems"]
"troubleshooting/common-problems"
]
}, },
{ {
"type": "category", "type": "category",
"label": "Reference / FAQ", "label": "Reference / FAQ",
"items": [ "items": ["reference/faq", "reference/documentation-style-guide"]
"reference/faq",
"reference/documentation-style-guide"
]
} }
] ]
} }

View File

@ -1,5 +1 @@
[ ["0.1.22", "0.1.21", "0.1.20"]
"0.1.22",
"0.1.21",
"0.1.20"
]

View File

@ -0,0 +1,116 @@
import { fireEvent, render, screen } from "@testing-library/react";
import type React from "react";
import { MemoryRouter } from "react-router-dom";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { trackEvent } from "@/lib/analytics";
import { App } from "./App";
import { useDemoInfo } from "./hooks/useDemoInfo";
vi.mock("./hooks/useDemoInfo", () => ({
useDemoInfo: vi.fn(),
}));
vi.mock("@/lib/analytics", () => ({
trackEvent: vi.fn(),
}));
vi.mock("react-transition-group", () => ({
SwitchTransition: ({ children }: { children: React.ReactNode }) => children,
CSSTransition: ({ children }: { children: React.ReactNode }) => children,
}));
vi.mock("@/components/ui/sonner", () => ({
Toaster: () => null,
}));
vi.mock("./components/BasicAuthPrompt", () => ({
BasicAuthPrompt: () => null,
}));
vi.mock("./components/OnboardingGate", () => ({
OnboardingGate: () => null,
}));
vi.mock("./pages/GmailOauthCallbackPage", () => ({
GmailOauthCallbackPage: () => null,
}));
vi.mock("./pages/HomePage", () => ({
HomePage: () => <div>overview</div>,
}));
vi.mock("./pages/InProgressBoardPage", () => ({
InProgressBoardPage: () => null,
}));
vi.mock("./pages/JobPage", () => ({
JobPage: () => null,
}));
vi.mock("./pages/OrchestratorPage", () => ({
OrchestratorPage: () => null,
}));
vi.mock("./pages/SettingsPage", () => ({
SettingsPage: () => null,
}));
vi.mock("./pages/TrackingInboxPage", () => ({
TrackingInboxPage: () => null,
}));
vi.mock("./pages/VisaSponsorsPage", () => ({
VisaSponsorsPage: () => null,
}));
describe("App demo banner", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("shows a Star repo link in demo mode and tracks click", () => {
vi.mocked(useDemoInfo).mockReturnValue({
demoMode: true,
resetCadenceHours: 6,
lastResetAt: null,
nextResetAt: null,
baselineVersion: null,
baselineName: null,
});
render(
<MemoryRouter initialEntries={["/overview"]}>
<App />
</MemoryRouter>,
);
const link = screen.getByRole("link", { name: /star .*repo/i });
expect(link).toHaveAttribute(
"href",
"https://github.com/DaKheera47/job-ops",
);
fireEvent.click(link);
expect(trackEvent).toHaveBeenCalledWith("star_repo_click", {
location: "demo_mode_banner",
});
});
it("does not render the demo banner CTA when demo mode is disabled", () => {
vi.mocked(useDemoInfo).mockReturnValue({
demoMode: false,
resetCadenceHours: 6,
lastResetAt: null,
nextResetAt: null,
baselineVersion: null,
baselineName: null,
});
render(
<MemoryRouter initialEntries={["/overview"]}>
<App />
</MemoryRouter>,
);
expect(screen.queryByRole("link", { name: /star .*repo/i })).toBeNull();
});
});

View File

@ -7,6 +7,7 @@ import { Navigate, Route, Routes, useLocation } from "react-router-dom";
import { CSSTransition, SwitchTransition } from "react-transition-group"; import { CSSTransition, SwitchTransition } from "react-transition-group";
import { Toaster } from "@/components/ui/sonner"; import { Toaster } from "@/components/ui/sonner";
import { trackEvent } from "@/lib/analytics";
import { BasicAuthPrompt } from "./components/BasicAuthPrompt"; import { BasicAuthPrompt } from "./components/BasicAuthPrompt";
import { OnboardingGate } from "./components/OnboardingGate"; import { OnboardingGate } from "./components/OnboardingGate";
import { useDemoInfo } from "./hooks/useDemoInfo"; import { useDemoInfo } from "./hooks/useDemoInfo";
@ -58,7 +59,19 @@ export const App: React.FC = () => {
{demoInfo?.demoMode && ( {demoInfo?.demoMode && (
<div className="w-full border-b border-amber-400/50 bg-amber-500/20 px-4 py-2 text-center text-xs text-amber-100 backdrop-blur"> <div className="w-full border-b border-amber-400/50 bg-amber-500/20 px-4 py-2 text-center text-xs text-amber-100 backdrop-blur">
Demo mode: integrations are simulated and data resets every{" "} Demo mode: integrations are simulated and data resets every{" "}
{demoInfo.resetCadenceHours} hours. {demoInfo.resetCadenceHours} hours.{" "}
<a
className="font-semibold underline underline-offset-2 hover:text-amber-50"
href="https://github.com/DaKheera47/job-ops"
target="_blank"
rel="noreferrer"
onClick={() =>
trackEvent("star_repo_click", { location: "demo_mode_banner" })
}
>
Star the repo on GitHub
</a>
.
</div> </div>
)} )}
<div> <div>