Skip to content
Merged
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
10 changes: 9 additions & 1 deletion .github/workflows/deploy-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ jobs:
NEXT_PUBLIC_SENTRY_DSN_API: ${{ secrets.NEXT_PUBLIC_SENTRY_DSN_API }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
NEXT_PUBLIC_SENTRY_ENVIRONMENT: ${{ vars.NEXT_PUBLIC_SENTRY_ENVIRONMENT }}
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
POSTHOG_PROJECT_ID: ${{ secrets.POSTHOG_PROJECT_ID }}
run: |
vercel pull --yes --environment=preview --token=$VERCEL_TOKEN
vercel build --token=$VERCEL_TOKEN
Expand All @@ -152,7 +154,9 @@ jobs:
--env GH_CLIENT_ID=$GH_CLIENT_ID \
--env GH_CLIENT_SECRET=$GH_CLIENT_SECRET \
--env NEXT_PUBLIC_SENTRY_DSN_API=$NEXT_PUBLIC_SENTRY_DSN_API \
--env NEXT_PUBLIC_SENTRY_ENVIRONMENT=$NEXT_PUBLIC_SENTRY_ENVIRONMENT)
--env NEXT_PUBLIC_SENTRY_ENVIRONMENT=$NEXT_PUBLIC_SENTRY_ENVIRONMENT \
--env POSTHOG_API_KEY=$POSTHOG_API_KEY \
--env POSTHOG_PROJECT_ID=$POSTHOG_PROJECT_ID)
vercel alias $VERCEL_URL ${{ env.API_ALIAS }} --scope=$VERCEL_ORG_ID --token=$VERCEL_TOKEN
echo "vercel_url=$VERCEL_URL" >> $GITHUB_OUTPUT

Expand Down Expand Up @@ -388,6 +392,8 @@ jobs:
NEXT_PUBLIC_COOKIE_DOMAIN: ${{ secrets.NEXT_PUBLIC_COOKIE_DOMAIN }}
NEXT_PUBLIC_POSTHOG_KEY: ${{ secrets.NEXT_PUBLIC_POSTHOG_KEY }}
NEXT_PUBLIC_POSTHOG_HOST: ${{ secrets.NEXT_PUBLIC_POSTHOG_HOST }}
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
POSTHOG_PROJECT_ID: ${{ secrets.POSTHOG_PROJECT_ID }}
NEXT_PUBLIC_SENTRY_DSN_ADMIN: ${{ secrets.NEXT_PUBLIC_SENTRY_DSN_ADMIN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
NEXT_PUBLIC_SENTRY_ENVIRONMENT: ${{ vars.NEXT_PUBLIC_SENTRY_ENVIRONMENT }}
Expand All @@ -404,6 +410,8 @@ jobs:
--env NEXT_PUBLIC_COOKIE_DOMAIN=$NEXT_PUBLIC_COOKIE_DOMAIN \
--env NEXT_PUBLIC_POSTHOG_KEY=$NEXT_PUBLIC_POSTHOG_KEY \
--env NEXT_PUBLIC_POSTHOG_HOST=$NEXT_PUBLIC_POSTHOG_HOST \
--env POSTHOG_API_KEY=$POSTHOG_API_KEY \
--env POSTHOG_PROJECT_ID=$POSTHOG_PROJECT_ID \
--env NEXT_PUBLIC_SENTRY_DSN_ADMIN=$NEXT_PUBLIC_SENTRY_DSN_ADMIN \
--env NEXT_PUBLIC_SENTRY_ENVIRONMENT=$NEXT_PUBLIC_SENTRY_ENVIRONMENT)
vercel alias $VERCEL_URL ${{ env.ADMIN_ALIAS }} --scope=$VERCEL_ORG_ID --token=$VERCEL_TOKEN
Expand Down
10 changes: 9 additions & 1 deletion .github/workflows/deploy-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ jobs:
NEXT_PUBLIC_SENTRY_DSN_API: ${{ secrets.NEXT_PUBLIC_SENTRY_DSN_API }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
NEXT_PUBLIC_SENTRY_ENVIRONMENT: ${{ vars.NEXT_PUBLIC_SENTRY_ENVIRONMENT }}
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
POSTHOG_PROJECT_ID: ${{ secrets.POSTHOG_PROJECT_ID }}
Comment on lines +87 to +88
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the deploy-production.yml file around lines 87-88
cat -n .github/workflows/deploy-production.yml | sed -n '80,120p'

Repository: superset-sh/superset

Length of output: 2415


🏁 Script executed:

# Search for POSTHOG_API_KEY usage in the codebase
rg -n "POSTHOG_API_KEY" --type ts --type js --type env -C 2

Repository: superset-sh/superset

Length of output: 91


🏁 Script executed:

# Search for POSTHOG_PROJECT_ID usage in the codebase
rg -n "POSTHOG_PROJECT_ID" --type ts --type js --type env -C 2

Repository: superset-sh/superset

Length of output: 91


🏁 Script executed:

# Check if there's env validation in trpc package or api directory
find . -type f \( -name "env.ts" -o -name "env.js" -o -name ".env*" \) | grep -E "(trpc|api|server)" | head -20

Repository: superset-sh/superset

Length of output: 112


🏁 Script executed:

# Look for PostHog setup in the API codebase
rg -n "posthog\|PostHog" --type ts --type js -C 2 | head -100

Repository: superset-sh/superset

Length of output: 46


🏁 Script executed:

cat -n ./apps/api/src/env.ts

Repository: superset-sh/superset

Length of output: 1430


🏁 Script executed:

cat -n ./packages/trpc/src/env.ts

Repository: superset-sh/superset

Length of output: 593


🏁 Script executed:

# Also search more broadly for posthog (case insensitive)
rg -i "posthog" --type ts --type js --type jsx --type tsx -C 3

Repository: superset-sh/superset

Length of output: 91


🏁 Script executed:

# Look for usage of these env variables in the tRPC package
rg -n "env\.|POSTHOG" packages/trpc --type ts -C 3

Repository: superset-sh/superset

Length of output: 3158


🏁 Script executed:

# Check deploy-admin section for similar setup
cat -n .github/workflows/deploy-production.yml | sed -n '265,300p'

Repository: superset-sh/superset

Length of output: 2478


🏁 Script executed:

# Search for how tRPC env is imported and used
rg -n "from.*trpc.*env|import.*env" apps/api --type ts -C 2

Repository: superset-sh/superset

Length of output: 2650


Ensure POSTHOG_API_KEY and POSTHOG_PROJECT_ID secrets are configured in GitHub repository settings.

These server-side credentials are required by the tRPC package for authenticated requests to PostHog's analytics API. Without these secrets configured, the deployment will fail during the build phase when the tRPC environment validation runs, or the PostHog integration will not function in production.

🤖 Prompt for AI Agents
.github/workflows/deploy-production.yml lines 87-88: the workflow references
POSTHOG_API_KEY and POSTHOG_PROJECT_ID secrets but they may not be configured in
the repository settings; add both secrets in the GitHub repository (Settings →
Secrets and variables → Actions) with the correct PostHog API key and project ID
values, then re-run the workflow to ensure the tRPC PostHog integration can
validate environment variables during build/deploy.

run: |
vercel pull --yes --environment=production --token=$VERCEL_TOKEN
vercel build --prod --token=$VERCEL_TOKEN
Expand All @@ -102,7 +104,9 @@ jobs:
--env GH_CLIENT_ID=$GH_CLIENT_ID \
--env GH_CLIENT_SECRET=$GH_CLIENT_SECRET \
--env NEXT_PUBLIC_SENTRY_DSN_API=$NEXT_PUBLIC_SENTRY_DSN_API \
--env NEXT_PUBLIC_SENTRY_ENVIRONMENT=$NEXT_PUBLIC_SENTRY_ENVIRONMENT
--env NEXT_PUBLIC_SENTRY_ENVIRONMENT=$NEXT_PUBLIC_SENTRY_ENVIRONMENT \
--env POSTHOG_API_KEY=$POSTHOG_API_KEY \
--env POSTHOG_PROJECT_ID=$POSTHOG_PROJECT_ID

deploy-web:
name: Deploy Web to Vercel
Expand Down Expand Up @@ -267,6 +271,8 @@ jobs:
NEXT_PUBLIC_COOKIE_DOMAIN: ${{ secrets.NEXT_PUBLIC_COOKIE_DOMAIN }}
NEXT_PUBLIC_POSTHOG_KEY: ${{ secrets.NEXT_PUBLIC_POSTHOG_KEY }}
NEXT_PUBLIC_POSTHOG_HOST: ${{ secrets.NEXT_PUBLIC_POSTHOG_HOST }}
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
POSTHOG_PROJECT_ID: ${{ secrets.POSTHOG_PROJECT_ID }}
NEXT_PUBLIC_SENTRY_DSN_ADMIN: ${{ secrets.NEXT_PUBLIC_SENTRY_DSN_ADMIN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
NEXT_PUBLIC_SENTRY_ENVIRONMENT: ${{ vars.NEXT_PUBLIC_SENTRY_ENVIRONMENT }}
Expand All @@ -283,6 +289,8 @@ jobs:
--env NEXT_PUBLIC_COOKIE_DOMAIN=$NEXT_PUBLIC_COOKIE_DOMAIN \
--env NEXT_PUBLIC_POSTHOG_KEY=$NEXT_PUBLIC_POSTHOG_KEY \
--env NEXT_PUBLIC_POSTHOG_HOST=$NEXT_PUBLIC_POSTHOG_HOST \
--env POSTHOG_API_KEY=$POSTHOG_API_KEY \
--env POSTHOG_PROJECT_ID=$POSTHOG_PROJECT_ID \
--env NEXT_PUBLIC_SENTRY_DSN_ADMIN=$NEXT_PUBLIC_SENTRY_DSN_ADMIN \
--env NEXT_PUBLIC_SENTRY_ENVIRONMENT=$NEXT_PUBLIC_SENTRY_ENVIRONMENT

Expand Down
1 change: 1 addition & 0 deletions apps/admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"react": "^19.2.3",
"react-dom": "^19.2.3",
"react-icons": "^5.5.0",
"recharts": "2.15.4",
"require-in-the-middle": "8.0.1",
"server-only": "^0.0.1",
"superjson": "^2.2.5",
Expand Down
Binary file added apps/admin/public/yc-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 39 additions & 12 deletions apps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,22 @@ import {
SidebarMenuItem,
SidebarRail,
} from "@superset/ui/sidebar";
import { LuChevronRight, LuHouse, LuUsers } from "react-icons/lu";
import { usePathname } from "next/navigation";
import { LuChevronRight, LuHouse, LuUsers, LuUserX } from "react-icons/lu";

import { AppSidebarHeader } from "./components/AppSidebarHeader";
import { NavUser } from "./components/NavUser";
import { SearchForm } from "./components/SearchForm";

const navigation = [
const topLevelNav = [
{
title: "Overview",
items: [
{
title: "Dashboard",
url: "/",
icon: LuHouse,
},
],
title: "Home",
url: "/",
icon: LuHouse,
},
];

const sections = [
{
title: "User Management",
items: [
Expand All @@ -47,6 +46,7 @@ const navigation = [
{
title: "Deleted Users",
url: "/users/deleted",
icon: LuUserX,
},
],
},
Expand All @@ -57,14 +57,38 @@ export interface AppSidebarProps extends React.ComponentProps<typeof Sidebar> {
}

export function AppSidebar({ user, ...props }: AppSidebarProps) {
const pathname = usePathname();

const isActive = (url: string) => {
if (url === "/") return pathname === "/";
return pathname.startsWith(url);
};

return (
<Sidebar {...props}>
<SidebarHeader>
<AppSidebarHeader />
<SearchForm />
</SidebarHeader>
<SidebarContent className="gap-0">
{navigation.map((section) => (
<SidebarGroup>
<SidebarGroupContent>
<SidebarMenu>
{topLevelNav.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild isActive={isActive(item.url)}>
<a href={item.url}>
<item.icon className="size-4" />
{item.title}
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>

{sections.map((section) => (
<Collapsible
key={section.title}
title={section.title}
Expand All @@ -86,7 +110,10 @@ export function AppSidebar({ user, ...props }: AppSidebarProps) {
<SidebarMenu>
{section.items.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild>
<SidebarMenuButton
asChild
isActive={isActive(item.url)}
>
<a href={item.url}>
{item.icon && <item.icon className="size-4" />}
{item.title}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export function AppSidebarHeader() {
/>
<div className="flex flex-col gap-0.5 leading-none">
<span className="font-medium">Superset</span>
<span className="">Admin Panel</span>
</div>
</a>
</SidebarMenuButton>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"use client";

import Image from "next/image";
import { useEffect, useState } from "react";

const DEMO_DAY = new Date("2026-06-16T00:00:00");

function calculateDaysLeft(): number {
const now = new Date();
const diff = DEMO_DAY.getTime() - now.getTime();
if (diff <= 0) return 0;
return diff / (1000 * 60 * 60 * 24);
}

export function DemoCountdown() {
const [daysLeft, setDaysLeft] = useState<number>(calculateDaysLeft);

useEffect(() => {
const timer = setInterval(() => {
setDaysLeft(calculateDaysLeft());
}, 100);

return () => clearInterval(timer);
}, []);
Comment on lines +18 to +24
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Excessive update frequency wastes CPU and battery.

The 100ms interval (line 20) updates the countdown 10 times per second, which is unnecessary for a day counter. Even with 6 decimal places, updating every second (1000ms) would provide smooth animation while being 10× more efficient.

This impacts performance, especially on mobile devices and battery-powered laptops.

Recommended fix: Change to 1 second interval
 	useEffect(() => {
 		const timer = setInterval(() => {
 			setDaysLeft(calculateDaysLeft());
-		}, 100);
+		}, 1000); // Update once per second
 
 		return () => clearInterval(timer);
 	}, []);

Alternatively, consider reducing decimal precision to 2-3 places and updating less frequently:

-	const fraction = (daysLeft - wholeDays).toFixed(6).slice(1); // ".466739"
+	const fraction = (daysLeft - wholeDays).toFixed(2).slice(1); // ".47"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
const timer = setInterval(() => {
setDaysLeft(calculateDaysLeft());
}, 100);
return () => clearInterval(timer);
}, []);
useEffect(() => {
const timer = setInterval(() => {
setDaysLeft(calculateDaysLeft());
}, 1000); // Update once per second
return () => clearInterval(timer);
}, []);
🤖 Prompt for AI Agents
In apps/admin/src/app/(dashboard)/components/DemoCountdown/DemoCountdown.tsx
around lines 18-24, the setInterval is currently 100ms which updates the day
counter 10×/sec and wastes CPU/battery; change the interval to 1000ms (1 second)
so the countdown updates once per second, keep the existing cleanup, and
optionally reduce decimal precision in calculateDaysLeft to 2-3 places (or
format the displayed value) to further lower update frequency and rendering
work.


const wholeDays = Math.floor(daysLeft);
const fraction = (daysLeft - wholeDays).toFixed(6).slice(1); // ".466739"

return (
<div className="flex items-center justify-end gap-3">
<Image src="/yc-logo.png" alt="Y Combinator" width={40} height={40} />
<div className="flex items-baseline font-mono" suppressHydrationWarning>
<span className="text-4xl font-bold">{wholeDays}</span>
<span className="text-xl text-muted-foreground">{fraction}</span>
<span className="ml-1 text-xl font-sans text-muted-foreground">
days
</span>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { DemoCountdown } from "./DemoCountdown";
126 changes: 126 additions & 0 deletions apps/admin/src/app/(dashboard)/components/FunnelChart/FunnelChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
"use client";

import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@superset/ui/card";
import {
type ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "@superset/ui/chart";
import { Skeleton } from "@superset/ui/skeleton";
import type { ReactNode } from "react";
import { Bar, BarChart, XAxis, YAxis } from "recharts";

interface FunnelStep {
name: string;
count: number;
conversionRate: number;
}

interface FunnelChartProps {
title: string;
description?: string;
data: FunnelStep[] | null | undefined;
isLoading?: boolean;
error?: { message: string } | null;
headerAction?: ReactNode;
}

const chartConfig = {
count: {
label: "Users",
color: "var(--chart-1)",
},
} satisfies ChartConfig;

export function FunnelChart({
title,
description,
data,
isLoading,
error,
headerAction,
}: FunnelChartProps) {
return (
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle>{title}</CardTitle>
{headerAction}
</div>
{description && <CardDescription>{description}</CardDescription>}
</CardHeader>
<CardContent>
{isLoading ? (
<div className="space-y-3">
<Skeleton className="h-6 w-full" />
<Skeleton className="h-6 w-4/5" />
<Skeleton className="h-6 w-3/5" />
<Skeleton className="h-6 w-2/5" />
</div>
) : error ? (
<div className="flex h-[200px] items-center justify-center">
<p className="text-destructive text-sm">
Failed to load funnel data
</p>
</div>
) : !data || data.length === 0 ? (
<div className="flex h-[200px] items-center justify-center rounded-md border border-dashed">
<p className="text-muted-foreground text-sm">
No funnel data available for this period
</p>
</div>
) : (
<ChartContainer config={chartConfig} className="h-[200px] w-full">
<BarChart
data={data}
layout="vertical"
margin={{ left: 0, right: 40 }}
>
<XAxis type="number" hide />
<YAxis
type="category"
dataKey="name"
tickLine={false}
axisLine={false}
width={120}
tick={{ fontSize: 12 }}
/>
<ChartTooltip
cursor={false}
content={
<ChartTooltipContent
formatter={(value, _name, item) => (
<div className="flex flex-col gap-1">
<span>{value.toLocaleString()} users</span>
<span className="text-muted-foreground">
{item.payload.conversionRate.toFixed(1)}% conversion
</span>
</div>
)}
/>
}
/>
<Bar
dataKey="count"
fill="var(--color-count)"
radius={[0, 4, 4, 0]}
label={{
position: "right",
fontSize: 12,
formatter: (value: number) => value.toLocaleString(),
}}
/>
</BarChart>
</ChartContainer>
)}
</CardContent>
</Card>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { FunnelChart } from "./FunnelChart";
Loading