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
58 changes: 58 additions & 0 deletions client-new/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Dependencies
node_modules/
.pnp
.pnp.js

# Build outputs
dist/
.vocs/
build/
out/

# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# Logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

# OS files
.DS_Store
Thumbs.db

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

# TypeScript
*.tsbuildinfo

# Testing
coverage/
.nyc_output

# Misc
*.log
.cache
.parcel-cache
.vercel
.turbo

# Temporary files
*.tmp
*.temp
.tmp/
.temp/
83 changes: 83 additions & 0 deletions client-new/components/CTA.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { ReactNode } from 'react'

interface CTAProps {
title: string
description?: string
children?: ReactNode
variant?: 'default' | 'gradient'
}

export function CTA({ title, description, children, variant = 'default' }: CTAProps) {
const bgClass = variant === 'gradient'
? 'bg-gradient-to-r from-dojo-primary to-dojo-light'
: 'bg-surface border border-dojo-primary/20'

const textClass = variant === 'gradient'
? 'text-white'
: 'text-text-primary'

const descClass = variant === 'gradient'
? 'text-white/90'
: 'text-text-secondary'

return (
<section className="py-16 md:py-24">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<div className={`${bgClass} rounded-2xl p-8 md:p-12 text-center`}>
<h2 className={`text-3xl md:text-4xl font-bold ${textClass} mb-4`}>
{title}
</h2>
{description && (
<p className={`text-lg ${descClass} mb-8 max-w-2xl mx-auto`}>
{description}
</p>
)}
{children && (
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
{children}
</div>
)}
</div>
</div>
</section>
)
}

interface CTAButtonProps {
href: string
children: ReactNode
variant?: 'primary' | 'white'
external?: boolean
}

export function CTAButton({ href, children, variant = 'primary', external = false }: CTAButtonProps) {
const className = variant === 'white'
? 'inline-flex items-center px-6 py-3 rounded-lg bg-white text-dojo-primary font-semibold hover:bg-gray-100 transition-colors shadow-lg'
: 'inline-flex items-center px-6 py-3 rounded-lg bg-dojo-primary text-white font-semibold hover:bg-dojo-hover transition-colors shadow-lg'

const content = (
<>
{children}
<span className="ml-2">→</span>
</>
)

if (external) {
return (
<a
href={href}
target="_blank"
rel="noopener noreferrer"
className={className}
>
{content}
</a>
)
}

return (
<a href={href} className={className}>
{content}
</a>
)
}
90 changes: 90 additions & 0 deletions client-new/components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React from 'react'

interface ErrorBoundaryState {
hasError: boolean
error?: Error
}

interface ErrorBoundaryProps {
children: React.ReactNode
fallback?: React.ComponentType<{ error?: Error; resetError?: () => void }>
}

class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props)
this.state = { hasError: false }
}

static getDerivedStateFromError(error: Error): ErrorBoundaryState {
return { hasError: true, error }
}

componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('ErrorBoundary caught an error:', error, errorInfo)
}

resetError = () => {
this.setState({ hasError: false, error: undefined })
}

render() {
if (this.state.hasError) {
const FallbackComponent = this.props.fallback || DefaultErrorFallback
return <FallbackComponent error={this.state.error} resetError={this.resetError} />
}

return this.props.children
}
}

interface ErrorFallbackProps {
error?: Error
resetError?: () => void
}

function DefaultErrorFallback({ error, resetError }: ErrorFallbackProps) {
return (
<div className="flex flex-col items-center justify-center min-h-[400px] p-8 text-center">
<div className="max-w-md mx-auto">
<div className="text-6xl mb-4">⚠️</div>
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
Something went wrong
</h2>
<p className="text-gray-600 dark:text-gray-400 mb-6">
We encountered an unexpected error. This has been logged and we'll look into it.
</p>

{error && (
<details className="mb-6 text-left">
<summary className="cursor-pointer text-sm text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">
Error details
</summary>
<pre className="mt-2 p-3 bg-gray-100 dark:bg-gray-800 rounded text-xs overflow-auto">
{error.message}
</pre>
</details>
)}

<div className="space-x-4">
{resetError && (
<button
onClick={resetError}
className="px-4 py-2 bg-[#ff3000] text-white rounded hover:bg-[#e02900] transition-colors"
>
Try again
</button>
)}
<button
onClick={() => window.location.reload()}
className="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
>
Reload page
</button>
</div>
</div>
</div>
)
}

export { ErrorBoundary, type ErrorBoundaryProps, type ErrorFallbackProps }
133 changes: 133 additions & 0 deletions client-new/components/Features.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { ReactNode } from 'react'

interface Feature {
icon: string | ReactNode
title: string
description: string
href?: string
}

interface FeaturesProps {
title?: string
subtitle?: string
features: Feature[]
columns?: 2 | 3 | 4
}

export function Features({ title, subtitle, features, columns = 3 }: FeaturesProps) {
const gridCols = {
2: 'grid-cols-1 md:grid-cols-2',
3: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
4: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-4',
}

return (
<section className="py-16 md:py-24">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
{(title || subtitle) && (
<div className="text-center mb-12">
{subtitle && (
<p className="text-dojo-primary font-semibold text-sm md:text-base uppercase tracking-wide mb-3">
{subtitle}
</p>
)}
{title && (
<h2 className="text-3xl md:text-4xl font-bold text-text-primary">
{title}
</h2>
)}
</div>
)}

<div className={`grid ${gridCols[columns]} gap-8`}>
{features.map((feature, index) => (
<FeatureCard key={index} {...feature} />
))}
</div>
</div>
</section>
)
}

interface FeatureCardProps extends Feature {}

export function FeatureCard({ icon, title, description, href }: FeatureCardProps) {
const content = (
<>
<div className="flex items-center justify-center w-12 h-12 rounded-lg bg-dojo-primary/10 text-dojo-primary text-2xl mb-4 group-hover:scale-110 transition-transform">
{icon}
</div>
<h3 className="text-xl font-semibold text-text-primary mb-2 group-hover:text-dojo-primary transition-colors">
{title}
</h3>
<p className="text-text-secondary">
{description}
</p>
{href && (
<div className="mt-4 text-dojo-primary font-medium">
Learn more →
</div>
)}
</>
)

if (href) {
return (
<a href={href} className="block p-6 bg-surface rounded-xl border border-border hover:border-dojo-primary/50 hover:shadow-lg transition-all duration-300 group">
{content}
</a>
)
}

return (
<div className="p-6 bg-surface rounded-xl border border-border">
{content}
</div>
)
}

interface FeatureHighlightProps {
icon: string | ReactNode
title: string
description: string
image?: string
reverse?: boolean
children?: ReactNode
}

export function FeatureHighlight({ icon, title, description, image, reverse = false, children }: FeatureHighlightProps) {
return (
<section className="py-16 md:py-24">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className={`grid grid-cols-1 lg:grid-cols-2 gap-12 items-center ${reverse ? 'lg:flex-row-reverse' : ''}`}>
<div className={reverse ? 'lg:order-2' : ''}>
<div className="flex items-center justify-center w-16 h-16 rounded-xl bg-dojo-primary/10 text-dojo-primary text-3xl mb-6">
{icon}
</div>
<h2 className="text-3xl md:text-4xl font-bold text-text-primary mb-4">
{title}
</h2>
<p className="text-lg text-text-secondary mb-6">
{description}
</p>
{children}
</div>

<div className={reverse ? 'lg:order-1' : ''}>
{image ? (
<img
src={image}
alt={title}
className="rounded-xl shadow-xl"
/>
) : (
<div className="aspect-video bg-gradient-to-br from-dojo-primary/20 to-dojo-primary/5 rounded-xl flex items-center justify-center">
<div className="text-6xl opacity-50">{icon}</div>
</div>
)}
</div>
</div>
</div>
</section>
)
}
Loading
Loading