Skip to content

Commit 9e8e878

Browse files
authored
feat(profile): add KiloClaw banner/CTA to profile page (#1382)
## Summary - Add a contextual KiloClaw banner to the `/profile` page that adapts based on the user's KiloClaw instance state - Users without a KiloClaw instance see a blue "Get started with KiloClaw" CTA with a brief description and a "Get Started" button linking to `/claw` - Users with an existing instance see a green "KiloClaw is running" banner with an "Go to KiloClaw" button linking to `/claw` - The banner is a client component that queries `trpc.kiloclaw.getBillingStatus` to detect instance state, consistent with the existing pattern in `src/app/(app)/claw/page.tsx` ## Verification - [x] `pnpm typecheck` — passes with no errors in root project - [x] `oxfmt --list-different .` — passes (code is formatted) - [x] `oxlint` — passes with 0 warnings and 0 errors ## Visual Changes - **No instance**: <img width="1136" height="579" alt="image" src="https://github.com/user-attachments/assets/53698d51-1928-44d1-a64d-1c93e75a6ebb" /> - **Has instance**: <img width="1202" height="546" alt="image" src="https://github.com/user-attachments/assets/72505b56-f678-4f2c-b056-665c7d78df97" /> ## Reviewer Notes - Positioned between the Kilo Pass section and the Organizations section for visibility without disrupting the existing credit/billing flow - Uses the same `trpc.kiloclaw.getBillingStatus` query that the `/claw` page uses, so no new API surface is introduced - Shows a subtle loading spinner while billing status loads; silently hides on error to avoid blocking the profile page - Design follows the existing banner pattern (e.g., `InsufficientBalanceBanner`, `BillingBanner`) using color-coded border/bg with icon, text, and action button
2 parents b282a56 + f9021af commit 9e8e878

2 files changed

Lines changed: 110 additions & 0 deletions

File tree

src/app/(app)/profile/page.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { ProfileKiloPassSection } from '@/components/profile/ProfileKiloPassSect
2323
import { CreateKilocodeOrgButton } from '@/components/dev/CreateKilocodeOrgButton';
2424
import { isFeatureFlagEnabled } from '@/lib/posthog-feature-flags';
2525
import { UserProfileCard } from '@/components/profile/UserProfileCard';
26+
import { ProfileKiloClawBanner } from '@/components/profile/ProfileKiloClawBanner';
2627

2728
export default async function ProfilePage({ searchParams }: AppPageProps) {
2829
const user = await getUserFromAuthOrRedirect('/users/sign_in');
@@ -42,6 +43,8 @@ export default async function ProfilePage({ searchParams }: AppPageProps) {
4243
return (
4344
// NOTE: When making changes to this structure, make sure to also update the structure in the loading.tsx file
4445
<PageLayout title="Profile">
46+
<ProfileKiloClawBanner />
47+
4548
<div className="flex w-full flex-col gap-4 lg:flex-row">
4649
<Card className="flex-1 rounded-xl shadow-sm">
4750
<CardContent className="p-6">
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
'use client';
2+
3+
import { useQuery } from '@tanstack/react-query';
4+
import { ArrowRight, Loader2, AlertTriangle } from 'lucide-react';
5+
import Link from 'next/link';
6+
import { useTRPC } from '@/lib/trpc/utils';
7+
import { Button } from '@/components/ui/button';
8+
import KiloCrabIcon from '@/components/KiloCrabIcon';
9+
10+
export function ProfileKiloClawBanner() {
11+
const trpc = useTRPC();
12+
const billingQuery = useQuery(trpc.kiloclaw.getBillingStatus.queryOptions());
13+
14+
if (billingQuery.isLoading) {
15+
return (
16+
<div className="flex w-full items-center justify-center rounded-lg border border-blue-500/20 bg-blue-500/5 p-6">
17+
<Loader2 className="text-muted-foreground h-5 w-5 animate-spin" />
18+
</div>
19+
);
20+
}
21+
22+
const billing = billingQuery.data;
23+
if (billingQuery.isError || !billing) {
24+
return null;
25+
}
26+
27+
const hasInstance = billing.instance !== null && billing.instance.exists;
28+
29+
if (hasInstance && billing.hasAccess) {
30+
return (
31+
<div className="flex w-full items-center gap-4 rounded-lg border border-emerald-500/30 bg-emerald-500/10 p-5">
32+
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-emerald-500/20">
33+
<KiloCrabIcon className="h-5 w-5 text-emerald-400" />
34+
</div>
35+
<div className="min-w-0 flex-1">
36+
<p className="text-sm font-semibold text-emerald-100">Your KiloClaw instance is active</p>
37+
<p className="text-sm text-emerald-300/80">
38+
Manage your instance, configure integrations, and monitor your Claw.
39+
</p>
40+
</div>
41+
<Button
42+
asChild
43+
variant="outline"
44+
className="shrink-0 border-emerald-500/40 text-emerald-200 hover:bg-emerald-500/20 hover:text-emerald-100"
45+
>
46+
<Link href="/claw">
47+
Go to KiloClaw
48+
<ArrowRight className="h-4 w-4" />
49+
</Link>
50+
</Button>
51+
</div>
52+
);
53+
}
54+
55+
if (hasInstance && !billing.hasAccess) {
56+
return (
57+
<div className="flex w-full items-center gap-4 rounded-lg border border-amber-500/30 bg-amber-500/10 p-5">
58+
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-amber-500/20">
59+
<AlertTriangle className="h-5 w-5 text-amber-400" />
60+
</div>
61+
<div className="min-w-0 flex-1">
62+
<p className="text-sm font-semibold text-amber-100">
63+
Your KiloClaw instance needs attention
64+
</p>
65+
<p className="text-sm text-amber-300/80">
66+
Your access has lapsed. Visit the dashboard to resolve billing and restore your
67+
instance.
68+
</p>
69+
</div>
70+
<Button
71+
asChild
72+
variant="outline"
73+
className="shrink-0 border-amber-500/40 text-amber-200 hover:bg-amber-500/20 hover:text-amber-100"
74+
>
75+
<Link href="/claw">
76+
Resolve
77+
<ArrowRight className="h-4 w-4" />
78+
</Link>
79+
</Button>
80+
</div>
81+
);
82+
}
83+
84+
return (
85+
<div className="flex w-full items-center gap-4 rounded-lg border border-blue-500/30 bg-blue-500/10 p-5">
86+
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-blue-500/20">
87+
<KiloCrabIcon className="h-5 w-5 text-blue-400" />
88+
</div>
89+
<div className="min-w-0 flex-1">
90+
<p className="text-sm font-semibold text-blue-100">Get started with KiloClaw</p>
91+
<p className="text-sm text-blue-300/80">
92+
Fully-managed OpenClaw, always online. Set up in minutes.
93+
</p>
94+
</div>
95+
<Button
96+
asChild
97+
variant="outline"
98+
className="shrink-0 border-blue-500/40 text-blue-200 hover:bg-blue-500/20 hover:text-blue-100"
99+
>
100+
<Link href="/claw">
101+
Get Started
102+
<ArrowRight className="h-4 w-4" />
103+
</Link>
104+
</Button>
105+
</div>
106+
);
107+
}

0 commit comments

Comments
 (0)