Skip to content
Open
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
3de8d8d
fix(ui): format API key creation date in onboarding step
thebeyondr Sep 17, 2025
b0f1637
chore(docs): update README for development setup instructions
thebeyondr Sep 17, 2025
ce81cdb
feat(ui): improve API keys interface with cleaner mobile layout
thebeyondr Sep 17, 2025
2f2b437
feat(ui): enhance API keys layout for better mobile experience
thebeyondr Sep 17, 2025
3b72bfc
chore(docs): update CLAUDE.md for clarity and structure
thebeyondr Sep 17, 2025
9524b2a
fix(ui): update badge styling for API keys list
thebeyondr Sep 17, 2025
ea0a5b4
feat(ui): introduce StatusBadge component for API and provider keys
thebeyondr Sep 17, 2025
b09bd74
chore(docs): refine development setup instructions
thebeyondr Sep 18, 2025
994aacd
feat(ui): add status filter and bulk activation for API keys
thebeyondr Sep 18, 2025
d90837a
feat(ui): update API keys client component for improved UX
thebeyondr Sep 18, 2025
e43ad73
feat(ui): refine API key onboarding component for better UX
thebeyondr Sep 18, 2025
344f531
feat(ui): enhance StatusBadge component with dynamic status icons
thebeyondr Sep 18, 2025
bfe0bc9
style(ui): enhance tooltip styling in API key components
thebeyondr Sep 18, 2025
02d3452
feat(ui): improve API keys list component functionality
thebeyondr Sep 18, 2025
d077842
feat(ui): enhance Stepper component with responsive design
thebeyondr Sep 18, 2025
47471f6
Merge branch 'main' into main
thebeyondr Sep 18, 2025
d6adc67
chore: merge branch 'main' of github.com:theopenco/llmgateway
thebeyondr Sep 22, 2025
2a5ddd6
feat(ui): add Geist fonts
thebeyondr Sep 24, 2025
e9d5760
feat(ui): enhance onboarding UX
thebeyondr Sep 24, 2025
d569bda
refactor(ui): simplify onboarding page by removing UserProvider
thebeyondr Sep 24, 2025
3fc6bd8
feat(ui): improve onboarding navigation and UX
thebeyondr Sep 24, 2025
c784606
feat(ui): improve plan choice step with new features
thebeyondr Sep 24, 2025
a88f9b2
fix(ui): imporve stepper layout and styling
thebeyondr Sep 24, 2025
1dd3eda
feat(ui): add API key management to welcome step
thebeyondr Sep 24, 2025
9bdccfd
feat(ui): add auto-complete to referral step
thebeyondr Sep 24, 2025
1505d20
fix(ui): update onboarding wizard text
thebeyondr Sep 24, 2025
1708e5b
Merge branch 'main' of github.com:theopenco/llmgateway
thebeyondr Sep 24, 2025
907edb4
Merge branch 'main' into fix/onboarding
thebeyondr Sep 24, 2025
df57c07
Merge branch 'main' into fix/onboarding
thebeyondr Sep 25, 2025
886b1d4
fix(ui): update font classNames
thebeyondr Sep 26, 2025
3cf666c
fix: remove implicit plan selection in onboarding wizard
thebeyondr Sep 26, 2025
fee4b29
fix(ui): add contact email to onboarding plan choice step
thebeyondr Sep 26, 2025
f621d6b
fix: migrate husky config to new location
thebeyondr Sep 26, 2025
85c624a
fix: complete onboarding before dashboard navigation
thebeyondr Sep 26, 2025
6363baf
Merge branch 'theopenco:main' into main
thebeyondr Sep 26, 2025
f892179
chore: merge branch 'main' into fix/onboarding
thebeyondr Sep 26, 2025
98a81a3
fix(husky): update commit-msg and pre-commit hooks to check for pnpm …
thebeyondr Sep 26, 2025
abc7b44
fix(husky): change shebang from sh to bash in hooks
thebeyondr Sep 26, 2025
13ef60f
fix(husky): streamline fnm loading in commit-msg and pre-commit hooks
thebeyondr Sep 26, 2025
79efb00
feat(ui): add utility functions for self-hosted environment detection
thebeyondr Sep 26, 2025
640e7ae
fix(ui): enhance onboarding plan choice step with self-hosting detection
thebeyondr Sep 26, 2025
e50daec
fix(ui): handle onboarding completion errors gracefully
thebeyondr Sep 26, 2025
2903a55
fix(ui): refactor onboarding step for self-hosting detection
thebeyondr Sep 26, 2025
af656fb
fix(husky): improve fnm loading and commit-msg handling
thebeyondr Sep 26, 2025
96d49b7
fix(ui): remove unused prop in onboarding wizard
thebeyondr Sep 26, 2025
b835855
fix(husky): remove fnm loading and pnpm checks from hooks
thebeyondr Sep 26, 2025
c0754ba
fix(ui): refactor onboarding steps to remove self-hosting utility
thebeyondr Sep 26, 2025
5d4c9e1
fix(ui): clean up tweet card component by removing unused prop
thebeyondr Sep 26, 2025
2ddeff2
fix(ui): update onboarding wizard to support free plan selection
thebeyondr Sep 26, 2025
9a4fcb0
fix(ui): format globals.css for improved readability
thebeyondr Sep 26, 2025
a3a531f
fix(ui): changed font-family from to
thebeyondr Sep 26, 2025
937e40e
fix(ui): -_- add missing newline at end of globals.css file
thebeyondr Sep 26, 2025
6b7f359
Merge branch 'theopenco:main' into fix/onboarding
thebeyondr Sep 26, 2025
b569e35
fix(ui): update font-family in globals.css and adjust layout structure
thebeyondr Sep 27, 2025
dfc274f
Merge branch 'fix/onboarding' of github.com:thebeyondr/omniport into …
thebeyondr Sep 27, 2025
36b7f64
Merge branch 'main' into fix/onboarding
thebeyondr Sep 29, 2025
db58dfc
fix(onboarding): update titles in onboarding wizard and plan choice step
thebeyondr Sep 29, 2025
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
13 changes: 13 additions & 0 deletions .husky/commit-msg
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
#!/usr/bin/env bash
# Check if pnpm is available
if ! command -v pnpm >/dev/null 2>&1; then
echo "Error: pnpm not found in PATH"
echo "Please install pnpm or add it to your PATH"
exit 1
fi

# Load fnm if available
if [ -f ~/.local/share/fnm/fnm ]; then
eval "$(~/.local/share/fnm/fnm env --shell bash)"
fi

pnpm commitlint --edit $1
13 changes: 13 additions & 0 deletions .husky/pre-commit
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
#!/usr/bin/env bash
# Check if pnpm is available
if ! command -v pnpm >/dev/null 2>&1; then
echo "Error: pnpm not found in PATH"
echo "Please install pnpm or add it to your PATH"
exit 1
fi

# Load fnm if available
if [ -f ~/.local/share/fnm/fnm ]; then
eval "$(~/.local/share/fnm/fnm env --shell bash)"
fi

pnpm lint-staged
8 changes: 6 additions & 2 deletions apps/ui/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
body {
@apply m-0;
font-family:
var(--font-geist),
-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu",
"Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
Expand All @@ -16,7 +17,7 @@ body {

code {
font-family:
source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
var(--font-geist-mono), source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
}

:root {
Expand Down Expand Up @@ -151,6 +152,7 @@ code {
* {
@apply border-border outline-ring/50;
}

body {
@apply bg-background text-foreground;
}
Expand All @@ -159,6 +161,7 @@ code {
from {
height: 0;
}

to {
height: var(--radix-accordion-content-height);
}
Expand All @@ -168,6 +171,7 @@ code {
from {
height: var(--radix-accordion-content-height);
}

to {
height: 0;
}
Expand All @@ -180,4 +184,4 @@ code {

@utility animate-accordion-up {
animation: accordion-up 0.2s ease-out;
}
}
40 changes: 36 additions & 4 deletions apps/ui/src/app/onboarding/onboarding-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,53 @@ import { useRouter } from "next/navigation";
import { useEffect } from "react";

import { OnboardingWizard } from "@/components/onboarding/onboarding-wizard";
import { UserProvider } from "@/components/providers/user-provider";
import { useUser } from "@/hooks/useUser";

export function OnboardingClient() {
const router = useRouter();
const { user } = useUser();
const { user, isLoading, error } = useUser();

useEffect(() => {
if (!user) {
if (!isLoading && !user && !error) {
router.push("/login");
}
}, [user, router]);
}, [user, isLoading, error, router]);

if (isLoading) {
return (
<div className="container mx-auto max-w-6xl py-10 flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto mb-4" />
<p className="text-muted-foreground">Loading...</p>
</div>
</div>
);
}

if (error) {
return (
<div className="container mx-auto max-w-6xl py-10 flex items-center justify-center">
<div className="text-center">
<p className="text-destructive mb-4">Failed to load user data</p>
<button
onClick={() => window.location.reload()}
className="px-4 py-2 bg-primary text-primary-foreground rounded"
>
Retry
</button>
</div>
</div>
);
}

if (!user) {
return null;
}

return <OnboardingWizard />;
return (
<UserProvider>
<OnboardingWizard />
</UserProvider>
);
}
20 changes: 3 additions & 17 deletions apps/ui/src/app/onboarding/page.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,8 @@
import { UserProvider } from "@/components/providers/user-provider";
import { fetchServerData } from "@/lib/server-api";

import { OnboardingClient } from "./onboarding-client";

import type { User } from "@/lib/types";

// Force dynamic rendering since this page uses server-side data fetching with cookies
// Force dynamic rendering since this page uses client-side data fetching
export const dynamic = "force-dynamic";

export default async function OnboardingPage() {
const initialUserData = await fetchServerData<{ user: User }>(
"GET",
"/user/me",
);

return (
<UserProvider initialUserData={initialUserData}>
<OnboardingClient />
</UserProvider>
);
export default function OnboardingPage() {
return <OnboardingClient />;
}
116 changes: 74 additions & 42 deletions apps/ui/src/components/onboarding/onboarding-wizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ import { usePostHog } from "posthog-js/react";
import * as React from "react";
import { useState } from "react";

import { Card, CardContent } from "@/lib/components/card";
import {
NavigationMenu,
NavigationMenuItem,
NavigationMenuList,
} from "@/lib/components/navigation-menu";
import { Stepper } from "@/lib/components/stepper";
import { useApi } from "@/lib/fetch-client";
import Logo from "@/lib/icons/Logo";
import { useStripe } from "@/lib/stripe";

import { ApiKeyStep } from "./api-key-step";
import { CreditsStep } from "./credits-step";
import { PlanChoiceStep } from "./plan-choice-step";
import { ProviderKeyStep } from "./provider-key-step";
Expand All @@ -27,16 +31,12 @@ const getSteps = (flowType: FlowType) => [
},
{
id: "referral",
title: "How did you hear about us?",
title: "How did you find us?",
optional: true,
},
{
id: "api-key",
title: "API Key",
},
Copy link
Member

Choose a reason for hiding this comment

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

why was this removed?

Copy link
Contributor Author

@thebeyondr thebeyondr Sep 29, 2025

Choose a reason for hiding this comment

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

Welcome step automatically makes the key or shows how many they have. Immediate reward and reduces the steps. Its something you'd have to try to see if it makes sense to you or not.

Welcome Step

  • Auto-generates API keys for new users
  • Detects self-hosting vs hosted context
  • Shows existing API key status with copy button
  • Skip option for users with existing keys
  • Better error handling with retry

{
id: "plan-choice",
title: "Choose Plan",
title: "Choose your approach",
},
{
id: flowType === "credits" ? "credits" : "provider-key",
Expand All @@ -49,9 +49,11 @@ export function OnboardingWizard() {
const [activeStep, setActiveStep] = useState(0);
const [flowType, setFlowType] = useState<FlowType>(null);
const [hasSelectedPlan, setHasSelectedPlan] = useState(false);
const [selectedPlanName, setSelectedPlanName] = useState<string>("");
const [isPaymentSuccessful, setIsPaymentSuccessful] = useState(false);
const [referralSource, setReferralSource] = useState<string>("");
const [referralDetails, setReferralDetails] = useState<string>("");

const router = useRouter();
const posthog = usePostHog();
const { stripe, isLoading: stripeLoading } = useStripe();
Expand All @@ -65,8 +67,14 @@ export function OnboardingWizard() {
const STEPS = getSteps(flowType);

const handleStepChange = async (step: number) => {
// Special handling for plan choice step (now at index 3)
if (activeStep === 3) {
// Handle backward navigation
if (step < activeStep) {
setActiveStep(step);
return;
}

// Special handling for plan choice step (now at index 2)
if (activeStep === 2) {
if (!hasSelectedPlan) {
// Skip to dashboard if no plan selected
posthog.capture("onboarding_skipped", {
Expand Down Expand Up @@ -103,37 +111,47 @@ export function OnboardingWizard() {
const handleSelectCredits = () => {
setFlowType("credits");
setHasSelectedPlan(true);
setActiveStep(4);
setSelectedPlanName("Buy Credits");
setActiveStep(3);
};

const handleSelectBYOK = () => {
setFlowType("byok");
setHasSelectedPlan(true);
setActiveStep(4);
setSelectedPlanName("Bring Your Own Keys");
setActiveStep(3);
};

const handleSelectFreePlan = () => {
setHasSelectedPlan(true);
setSelectedPlanName("Free Plan");
// Continue to next step or complete onboarding
setActiveStep(3);
};

const handleReferralComplete = (source: string, details?: string) => {
setReferralSource(source);
if (details) {
setReferralDetails(details);
}
setActiveStep(2); // Move to API Key step
setActiveStep(2); // Move to Plan Choice step
};

// Special handling for PlanChoiceStep to pass callbacks
const renderCurrentStep = () => {
if (activeStep === 3) {
if (activeStep === 2) {
return (
<PlanChoiceStep
onSelectCredits={handleSelectCredits}
onSelectBYOK={handleSelectBYOK}
onSelectFreePlan={handleSelectFreePlan}
hasSelectedPlan={hasSelectedPlan}
/>
);
}

// For credits step, wrap with Stripe Elements
if (activeStep === 4 && flowType === "credits") {
if (activeStep === 3 && flowType === "credits") {
return stripeLoading ? (
<div className="p-6 text-center">Loading payment form...</div>
) : (
Expand All @@ -144,7 +162,7 @@ export function OnboardingWizard() {
}

// For BYOK step
if (activeStep === 4 && flowType === "byok") {
if (activeStep === 3 && flowType === "byok") {
return <ProviderKeyStep />;
}

Expand All @@ -157,24 +175,29 @@ export function OnboardingWizard() {
return <ReferralStep onComplete={handleReferralComplete} />;
}

if (activeStep === 2) {
return <ApiKeyStep />;
}

return null;
};

// Customize stepper steps to show appropriate button text
const getStepperSteps = () => {
return STEPS.map((step, index) => ({
...step,
// Make plan choice step show Skip when no selection
...(index === 3 &&
!hasSelectedPlan && {
customNextText: "Skip",
}),
// Welcome step shows dynamic text based on user state
...(index === 0 && {
customNextText: "Next: How did you find us?",
}),
// Referral step shows dynamic text based on user state
...(index === 1 && {
customNextText: "Next: Choose your approach",
}),
// Make plan choice step show dynamic text based on selected plan
...(index === 2 && {
customNextText: hasSelectedPlan
? `Continue with ${selectedPlanName}`
: "Skip",
}),
// Remove optional status from credits step when payment is successful
...(index === 4 &&
...(index === 3 &&
flowType === "credits" &&
isPaymentSuccessful && {
optional: false,
Expand All @@ -183,22 +206,31 @@ export function OnboardingWizard() {
};

return (
<div className="container mx-auto max-w-3xl py-10">
<Card>
<CardContent className="p-6 sm:p-8">
<Stepper
steps={getStepperSteps()}
activeStep={activeStep}
onStepChange={handleStepChange}
className="mb-6"
nextButtonDisabled={
activeStep === 4 && flowType === "credits" && !isPaymentSuccessful
}
>
{renderCurrentStep()}
</Stepper>
</CardContent>
</Card>
<div className="container mx-auto px-4 py-10">
<NavigationMenu className="mx-auto">
<NavigationMenuList>
<NavigationMenuItem asChild>
<div className="flex items-center space-x-2">
<Logo className="h-8 w-8 rounded-full text-black dark:text-white" />
<span className="text-xl font-bold tracking-tight text-zinc-900 dark:text-white">
LLM Gateway
</span>
</div>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>

<Stepper
steps={getStepperSteps()}
activeStep={activeStep}
onStepChange={handleStepChange}
className="mb-6"
nextButtonDisabled={
activeStep === 3 && flowType === "credits" && !isPaymentSuccessful
}
>
{renderCurrentStep()}
</Stepper>
</div>
);
}
Loading
Loading