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
49 changes: 22 additions & 27 deletions app/bots/BotNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Link from 'next/link';
import type { Route } from 'next';
import { Dialog, Transition } from '@headlessui/react';
import { BotSwitcher, SiteMenuDropdown } from '@/components/navigation';
import { Logo } from '@/components/shared/Logo';
import { menuItems } from '@/data/menuItems';
import { useScrollNavigation, getNavColors } from '@/lib/hooks';
import type { BotMenuItem } from '@/types/bot';
Expand Down Expand Up @@ -53,11 +54,11 @@ export const BotNavigation: React.FC<BotNavigationProps> = ({
};

// Get color classes from centralized config
const colors = getNavColors(accentColor);
const colors = getNavColors();

const navClasses = isScrolled
? 'bg-white shadow-md border-b border-gray-200'
: 'bg-white border-b border-gray-200';
? 'bg-surface shadow-md border-b border-edge'
: 'bg-surface border-b border-edge';

return (
<nav
Expand All @@ -67,17 +68,11 @@ export const BotNavigation: React.FC<BotNavigationProps> = ({
<div className="flex justify-between items-center h-12">
{/* Left side: Botsmann logo + divider + Bot Switcher */}
<div className="flex items-center h-full">
{/* Botsmann 'B' logo - links to home */}
<Link
href="/"
className="flex items-center justify-center w-9 h-9 bg-gradient-to-r from-blue-600 to-purple-600 text-white font-bold text-sm rounded-lg hover:opacity-90 transition-opacity"
aria-label="Go to Botsmann home"
>
B
</Link>
{/* Botsmann 'B' mark — the shared Logo, so it cannot drift again */}
<Logo href="/" showText={false} size="sm" />

{/* Vertical divider */}
<div className="h-6 w-px bg-gray-300 mx-3" />
<div className="h-6 w-px bg-edge mx-3" />

{/* Bot Switcher dropdown */}
<BotSwitcher
Expand All @@ -95,7 +90,7 @@ export const BotNavigation: React.FC<BotNavigationProps> = ({
key={item.id}
onClick={() => handleScrollToSection(item.section)}
className={`px-3 py-2 rounded-md text-sm font-medium transition-colors whitespace-nowrap flex items-center ${
activeSection === item.section ? colors.active : `text-gray-600 ${colors.hover}`
activeSection === item.section ? colors.active : `text-ink-muted ${colors.hover}`
} ${item.id === 'roadmap' ? `border ${colors.border}` : ''}`}
>
{item.icon && <span className="mr-1">{item.icon}</span>}
Expand All @@ -108,7 +103,7 @@ export const BotNavigation: React.FC<BotNavigationProps> = ({
<div className="flex items-center gap-2">
{/* Mobile menu button */}
<button
className="md:hidden p-2 rounded-md text-gray-600 hover:text-gray-800 hover:bg-gray-100 focus:outline-none"
className="md:hidden p-2 rounded-md text-ink-muted hover:text-ink hover:bg-paper focus:outline-none"
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
aria-label={isMobileMenuOpen ? 'Close menu' : 'Open menu'}
>
Expand Down Expand Up @@ -182,20 +177,20 @@ export const BotNavigation: React.FC<BotNavigationProps> = ({
leaveTo="translate-x-full"
>
<Dialog.Panel className="pointer-events-auto w-screen max-w-md">
<div className="flex h-full flex-col overflow-y-scroll bg-white shadow-xl">
<div className="flex h-full flex-col overflow-y-scroll bg-surface shadow-xl">
{/* Header with Botsmann branding */}
<div className="flex items-center justify-between px-4 py-6 border-b border-gray-200 bg-gradient-to-r from-blue-50 to-purple-50">
<div className="flex items-center justify-between px-4 py-6 border-b border-edge bg-gradient-to-r from-blue-50 to-purple-50">
<div className="flex items-center gap-3">
<div className="flex items-center justify-center w-9 h-9 bg-gradient-to-r from-blue-600 to-purple-600 text-white font-bold text-sm rounded-lg">
B
</div>
<Dialog.Title className="text-lg font-semibold text-gray-900">
<Dialog.Title className="text-lg font-semibold text-ink">
Botsmann
</Dialog.Title>
</div>
<button
type="button"
className="rounded-md p-2 text-gray-400 hover:bg-white/50"
className="rounded-md p-2 text-ink-muted hover:bg-surface/50"
onClick={() => setIsMobileMenuOpen(false)}
>
<span className="sr-only">Close menu</span>
Expand All @@ -221,7 +216,7 @@ export const BotNavigation: React.FC<BotNavigationProps> = ({
{/* Home link */}
<Link
href="/"
className="flex items-center px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:bg-gray-100"
className="flex items-center px-3 py-2 rounded-md text-base font-medium text-ink hover:bg-paper"
onClick={() => setIsMobileMenuOpen(false)}
>
<svg
Expand All @@ -243,7 +238,7 @@ export const BotNavigation: React.FC<BotNavigationProps> = ({
{/* All Bots link */}
<Link
href="/bots"
className="flex items-center px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:bg-gray-100"
className="flex items-center px-3 py-2 rounded-md text-base font-medium text-ink hover:bg-paper"
onClick={() => setIsMobileMenuOpen(false)}
>
<svg
Expand All @@ -262,27 +257,27 @@ export const BotNavigation: React.FC<BotNavigationProps> = ({
All Bots
</Link>

<hr className="my-4 border-gray-200" />
<hr className="my-4 border-edge" />

{/* Site Navigation */}
<p className="px-3 text-xs font-medium text-gray-500 uppercase tracking-wider mb-2">
<p className="px-3 text-xs font-medium text-ink-muted uppercase tracking-wider mb-2">
Botsmann
</p>
{siteNavItems.map((item) => (
<Link
key={item.label}
href={item.path}
className="flex items-center px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:bg-gray-100"
className="flex items-center px-3 py-2 rounded-md text-base font-medium text-ink hover:bg-paper"
onClick={() => setIsMobileMenuOpen(false)}
>
<span>{item.label}</span>
</Link>
))}

<hr className="my-4 border-gray-200" />
<hr className="my-4 border-edge" />

{/* Bot Section links */}
<p className="px-3 text-xs font-medium text-gray-500 uppercase tracking-wider mb-2">
<p className="px-3 text-xs font-medium text-ink-muted uppercase tracking-wider mb-2">
{botTitle} Sections
</p>
{sectionMenuItems.map((item) => (
Expand All @@ -292,7 +287,7 @@ export const BotNavigation: React.FC<BotNavigationProps> = ({
className={`w-full text-left flex items-center px-3 py-2 rounded-md text-base font-medium transition-colors ${
activeSection === item.section
? colors.active
: `text-gray-700 hover:bg-gray-100`
: `text-ink hover:bg-paper`
}`}
>
{item.icon && <span className="mr-3 text-lg">{item.icon}</span>}
Expand All @@ -302,7 +297,7 @@ export const BotNavigation: React.FC<BotNavigationProps> = ({

{chatLink && (
<>
<hr className="my-4 border-gray-200" />
<hr className="my-4 border-edge" />
<Link
href={chatLink as Route}
className={`w-full flex items-center justify-center px-4 py-3 ${colors.accent} text-white text-base font-medium rounded-md transition-colors`}
Expand Down
38 changes: 11 additions & 27 deletions app/bots/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import type {
DemoContent,
SectionConfig,
} from '@/lib/config/bot-pages';
import type { BotAccentColor } from '@/types/bot';

/**
* Dynamic bot page that renders from config
Expand All @@ -52,16 +51,14 @@ export default function BotPage() {

const rawTryLink = getBotTryLink(bot);
const tryLink = rawTryLink || '#waitlist';
const accentColor = pageConfig.accentColor;

return (
<div className="min-h-screen bg-white">
<div className="min-h-screen bg-paper">
<BotNavigation
botSlug={bot.slug}
botTitle={bot.nav.navTitle}
botEmoji={bot.nav.emoji}
botDescription={bot.nav.navDescription}
accentColor={bot.nav.accentColor}
menuItems={bot.nav.menuItems}
chatLink={tryLink}
/>
Expand All @@ -71,7 +68,6 @@ export default function BotPage() {
<SectionRenderer
key={section.id}
section={section}
accentColor={accentColor}
botSlug={slug}
tryLink={tryLink}
heroConfig={heroConfig}
Expand All @@ -87,19 +83,12 @@ export default function BotPage() {
*/
interface SectionRendererProps {
section: SectionConfig;
accentColor: BotAccentColor;
botSlug: string;
tryLink: string;
heroConfig: ReturnType<typeof getBotHeroConfig>;
}

function SectionRenderer({
section,
accentColor,
botSlug,
tryLink,
heroConfig,
}: SectionRendererProps) {
function SectionRenderer({ section, botSlug, tryLink, heroConfig }: SectionRendererProps) {
const { id, type, content, isLast } = section;

// Hero section uses external config
Expand All @@ -109,13 +98,13 @@ function SectionRenderer({
...heroConfig,
primaryCTA: { ...heroConfig.primaryCTA, href: tryLink },
};
return <BotHeroSection config={config} accentColor={accentColor} />;
return <BotHeroSection config={config} />;
}

// All other sections wrapped in BotSection
return (
<BotSection id={id} isLast={isLast}>
{renderSectionContent(type, content, accentColor, botSlug, tryLink)}
{renderSectionContent(type, content, botSlug, tryLink)}
</BotSection>
);
}
Expand All @@ -126,7 +115,6 @@ function SectionRenderer({
function renderSectionContent(
type: SectionConfig['type'],
content: SectionConfig['content'],
accentColor: BotAccentColor,
botSlug: string,
tryLink: string,
): React.ReactNode {
Expand All @@ -140,29 +128,25 @@ function renderSectionContent(
return <DemoSectionWrapper content={content as DemoContent} botSlug={botSlug} />;

case 'features':
return <FeaturesSection content={content as FeaturesContent} accentColor={accentColor} />;
return <FeaturesSection content={content as FeaturesContent} />;

case 'how-it-works':
return <HowItWorksSection content={content as HowItWorksContent} accentColor={accentColor} />;
return <HowItWorksSection content={content as HowItWorksContent} />;

case 'benefits':
return <BenefitsSection content={content as BenefitsContent} accentColor={accentColor} />;
return <BenefitsSection content={content as BenefitsContent} />;

case 'testimonials':
return (
<TestimonialsSection content={content as TestimonialsContent} accentColor={accentColor} />
);
return <TestimonialsSection content={content as TestimonialsContent} />;

case 'vision':
return <VisionSection content={content as VisionContent} accentColor={accentColor} />;
return <VisionSection content={content as VisionContent} />;

case 'technology':
return <TechnologySection content={content as TechnologyContent} accentColor={accentColor} />;
return <TechnologySection content={content as TechnologyContent} />;

case 'cta':
return (
<CTASection content={content as CTAContent} accentColor={accentColor} tryLink={tryLink} />
);
return <CTASection content={content as CTAContent} tryLink={tryLink} />;

default:
return null;
Expand Down
31 changes: 14 additions & 17 deletions components/shared/BotHeroSection.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
'use client';

import { type FC, type ReactNode } from 'react';
import type { BotAccentColor } from '@/types/bot';
import { ACCENT_HERO_CLASSES } from '@/lib/config/colors';
import { BRAND_HERO } from '@/lib/config/colors';

/**
* Chat message for the hero preview
Expand Down Expand Up @@ -69,7 +68,6 @@ export interface BotHeroConfig {

interface BotHeroSectionProps {
config: BotHeroConfig;
accentColor: BotAccentColor;
/** Optional custom content for the right panel instead of chat preview */
rightPanel?: ReactNode;
}
Expand All @@ -93,12 +91,11 @@ interface BotHeroSectionProps {
* { role: 'user', content: 'I need a summary of recent data privacy cases.' },
* ],
* }}
* accentColor="blue"
* />
* ```
*/
export const BotHeroSection: FC<BotHeroSectionProps> = ({ config, accentColor, rightPanel }) => {
const colors = ACCENT_HERO_CLASSES[accentColor];
export const BotHeroSection: FC<BotHeroSectionProps> = ({ config, rightPanel }) => {
const colors = BRAND_HERO;
const {
badge,
title,
Expand Down Expand Up @@ -126,7 +123,7 @@ export const BotHeroSection: FC<BotHeroSectionProps> = ({ config, accentColor, r
</div>
)}

<h1 className="text-3xl sm:text-4xl lg:text-5xl font-bold text-gray-900 mb-3 sm:mb-4">
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-bold text-ink mb-3 sm:mb-4">
{title}
{titleSuffix && (
<>
Expand All @@ -136,7 +133,7 @@ export const BotHeroSection: FC<BotHeroSectionProps> = ({ config, accentColor, r
)}
</h1>

<p className="text-base sm:text-lg lg:text-xl text-gray-700 mb-4 sm:mb-6">{subtitle}</p>
<p className="text-base sm:text-lg lg:text-xl text-ink mb-4 sm:mb-6">{subtitle}</p>

<div className="flex flex-col sm:flex-row gap-3 sm:gap-4">
<a
Expand Down Expand Up @@ -167,12 +164,12 @@ export const BotHeroSection: FC<BotHeroSectionProps> = ({ config, accentColor, r
{/* Mobile key benefits */}
{showMobileBenefits && keyBenefits && keyBenefits.length > 0 && (
<div className="mt-8 md:hidden">
<h3 className="text-lg font-semibold text-gray-800 mb-3">Key Benefits:</h3>
<h3 className="text-lg font-semibold text-ink mb-3">Key Benefits:</h3>
<ul className="space-y-2">
{keyBenefits.slice(0, 3).map((benefit, idx) => (
<li key={idx} className="flex items-start">
<span className="text-green-500 mr-2">✓</span>
<span className="text-gray-700">{benefit.text}</span>
<span className="text-ink">{benefit.text}</span>
</li>
))}
</ul>
Expand Down Expand Up @@ -205,8 +202,8 @@ export const BotHeroSection: FC<BotHeroSectionProps> = ({ config, accentColor, r
<span className="text-xl md:text-2xl">{botInfo.emoji}</span>
</div>
<div>
<h3 className="font-semibold text-gray-900">{botInfo.name}</h3>
<p className="text-sm text-gray-600">{botInfo.description}</p>
<h3 className="font-semibold text-ink">{botInfo.name}</h3>
<p className="text-sm text-ink-muted">{botInfo.description}</p>
</div>
</div>

Expand All @@ -217,24 +214,24 @@ export const BotHeroSection: FC<BotHeroSectionProps> = ({ config, accentColor, r
key={idx}
className={`p-3 rounded-lg shadow-sm ${
message.role === 'bot'
? 'bg-white ml-auto max-w-[85%]'
? 'bg-surface ml-auto max-w-[85%]'
: `${colors.userBubble} max-w-[85%]`
}`}
>
<p className="text-gray-800 text-sm md:text-base">{message.content}</p>
<p className="text-ink text-sm md:text-base">{message.content}</p>
</div>
))}
</div>

{/* Desktop key benefits inside chat panel */}
{showMobileBenefits && keyBenefits && keyBenefits.length > 0 && (
<div className="mt-6 hidden md:block">
<h4 className="font-medium text-gray-900 mb-2">Key Benefits:</h4>
<h4 className="font-medium text-ink mb-2">Key Benefits:</h4>
<div className="grid grid-cols-2 gap-2">
{keyBenefits.slice(0, 4).map((benefit, idx) => (
<div key={idx} className="bg-white p-2 rounded-md flex items-center">
<div key={idx} className="bg-surface p-2 rounded-md flex items-center">
<span className="text-green-500 mr-2">✓</span>
<span className="text-sm text-gray-700">{benefit.text}</span>
<span className="text-sm text-ink">{benefit.text}</span>
</div>
))}
</div>
Expand Down
Loading
Loading