cre-sync/app/(app)/dfy/page.tsx
BusyBee3333 4e6467ffb0 Add CRESync CRM application with Setup page
- Build complete Next.js CRM for commercial real estate
- Add authentication with JWT sessions and role-based access
- Add GoHighLevel API integration for contacts, conversations, opportunities
- Add AI-powered Control Center with tool calling
- Add Setup page with onboarding checklist (/setup)
- Add sidebar navigation with Setup menu item
- Fix type errors in onboarding API, GHL services, and control center tools
- Add Prisma schema with SQLite for local development
- Add UI components with clay morphism design system

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 17:30:55 -05:00

115 lines
3.8 KiB
TypeScript

'use client';
import { useState } from 'react';
import { DFY_PRODUCTS } from '@/lib/stripe/dfy-products';
import { Check, Loader2, Clock, Sparkles } from 'lucide-react';
export default function DFYServicesPage() {
const [loading, setLoading] = useState<string | null>(null);
const handleCheckout = async (productId: string) => {
setLoading(productId);
try {
const response = await fetch('/api/v1/stripe/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ productId }),
});
const data = await response.json();
if (data.checkoutUrl) {
window.location.href = data.checkoutUrl;
}
} catch (error) {
console.error('Checkout error:', error);
} finally {
setLoading(null);
}
};
const formatPrice = (cents: number) => {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
}).format(cents / 100);
};
return (
<div className="space-y-8">
<div>
<div className="flex items-center gap-3 mb-2">
<Sparkles className="text-primary-600" size={28} />
<h2 className="text-2xl font-bold text-slate-900">Done-For-You Services</h2>
</div>
<p className="text-slate-600">
Let our experts handle the setup while you focus on closing deals
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{Object.entries(DFY_PRODUCTS).map(([key, product]) => (
<div
key={key}
className="clay-card flex flex-col"
>
<div className="flex-1">
<h3 className="text-xl font-bold text-slate-900">{product.name}</h3>
<p className="text-slate-600 mt-2 text-sm">{product.description}</p>
<div className="mt-4">
<span className="text-3xl font-bold text-primary-600">
{formatPrice(product.priceInCents)}
</span>
<span className="text-slate-500 ml-2">one-time</span>
</div>
<ul className="mt-4 space-y-2">
{product.features.map((feature, i) => (
<li key={i} className="flex items-center text-sm text-slate-600">
<Check className="w-4 h-4 mr-2 text-success-500 flex-shrink-0" />
{feature}
</li>
))}
</ul>
<div className="flex items-center text-xs text-slate-500 mt-4">
<Clock className="w-3 h-3 mr-1" />
Delivery: {product.deliveryDays} business days
</div>
</div>
<button
onClick={() => handleCheckout(key)}
disabled={loading === key}
className="btn-primary w-full mt-6 flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed"
>
{loading === key ? (
<>
<Loader2 className="w-4 h-4 animate-spin" />
Processing...
</>
) : (
'Get Started'
)}
</button>
</div>
))}
</div>
<div className="clay-card bg-gradient-to-r from-primary-50 to-primary-100/50">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h3 className="text-lg font-semibold text-slate-900">Need a custom solution?</h3>
<p className="text-sm text-slate-600 mt-1">
Contact us for enterprise packages and custom integrations
</p>
</div>
<button className="btn-secondary whitespace-nowrap">
Contact Sales
</button>
</div>
</div>
</div>
);
}