Rocky_Mountain_Vending/components/stripe-status.tsx
DMleadgen 46d973904b
Initial commit: Rocky Mountain Vending website
Next.js website for Rocky Mountain Vending company featuring:
- Product catalog with Stripe integration
- Service areas and parts pages
- Admin dashboard with Clerk authentication
- SEO optimized pages with JSON-LD structured data

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 16:22:15 -07:00

361 lines
12 KiB
TypeScript

'use client'
import { useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import {
CheckCircle,
XCircle,
AlertTriangle,
RefreshCw,
ExternalLink,
Package,
ShoppingCart,
CreditCard
} from 'lucide-react'
interface StripeStatus {
success: boolean
account?: {
id: string
name: string
email: string
country: string
charges_enabled: boolean
payouts_enabled: boolean
}
products?: {
total: number
active: number
sample: Array<{
id: string
name: string
description?: string
price?: {
id: string
unit_amount: number
currency: string
}
}>
}
paymentMethods?: {
total: number
types: string[]
}
recentInvoices: number
environment: string
apiVersion: string
timestamp: string
error?: string
}
export function StripeStatus() {
const [status, setStatus] = useState<StripeStatus | null>(null)
const [loading, setLoading] = useState(true)
const [lastUpdated, setLastUpdated] = useState<string>('')
const fetchStatus = async () => {
try {
const response = await fetch('/api/stripe/test', {
method: 'POST',
cache: 'no-store'
})
if (response.ok) {
const data = await response.json()
setStatus(data)
setLastUpdated(new Date().toLocaleTimeString())
} else {
setStatus({
success: false,
error: 'Failed to fetch status',
timestamp: new Date().toISOString(),
recentInvoices: 0,
environment: process.env.NODE_ENV || 'development',
apiVersion: 'unknown'
})
}
} catch (error) {
setStatus({
success: false,
error: error instanceof Error ? error.message : 'Network error',
timestamp: new Date().toISOString(),
recentInvoices: 0,
environment: process.env.NODE_ENV || 'development',
apiVersion: 'unknown'
})
} finally {
setLoading(false)
}
}
useEffect(() => {
fetchStatus()
const interval = setInterval(fetchStatus, 30000) // Refresh every 30 seconds
return () => clearInterval(interval)
}, [])
if (loading) {
return (
<Card>
<CardContent className="p-4 flex items-center justify-center">
<RefreshCw className="h-4 w-4 animate-spin mr-2" />
<span className="text-sm">Checking Stripe connection...</span>
</CardContent>
</Card>
)
}
if (!status) {
return (
<Card>
<CardContent className="p-4">
<div className="flex items-center gap-2 text-red-600">
<XCircle className="h-4 w-4" />
<span className="text-sm">Unable to connect to Stripe</span>
</div>
</CardContent>
</Card>
)
}
const stripeWorking = status.success
return (
<div className="space-y-4">
{/* Status Overview */}
<Card className={stripeWorking ? 'border-green-200' : 'border-red-200'}>
<CardHeader className="pb-2">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
{stripeWorking ? (
<CheckCircle className="h-5 w-5 text-green-600" />
) : (
<XCircle className="h-5 w-5 text-red-600" />
)}
<CardTitle className="text-lg">
Stripe {stripeWorking ? 'Connected' : 'Not Connected'}
</CardTitle>
</div>
<Button
variant="outline"
size="sm"
onClick={fetchStatus}
className="flex items-center gap-1"
>
<RefreshCw className={`h-3 w-3 ${loading ? 'animate-spin' : ''}`} />
Refresh
</Button>
</div>
<CardDescription className="text-xs">
Last updated: {lastUpdated}
</CardDescription>
</CardHeader>
<CardContent>
{stripeWorking ? (
<p className="text-green-700">
Your Stripe account is connected and ready for products.
</p>
) : (
<div className="space-y-2">
<p className="text-red-700">
{status.error || 'Unable to connect to Stripe. Please check your configuration.'}
</p>
<div className="flex items-center gap-2">
<Badge variant="outline" className="text-yellow-600">
<AlertTriangle className="h-3 w-3 mr-1" />
Setup Required
</Badge>
<Button size="sm" variant="outline" asChild>
<a href="https://dashboard.stripe.com/login">
<ExternalLink className="h-3 w-3 mr-1" />
Go to Stripe
</a>
</Button>
</div>
</div>
)}
</CardContent>
</Card>
{/* Account Info */}
{stripeWorking && status.account && (
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-base flex items-center gap-2">
<CreditCard className="h-4 w-4" />
Account Information
</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<div className="grid grid-cols-2 gap-4 text-sm">
<div>
<span className="text-muted-foreground">Account ID:</span>
<div className="font-medium">{status.account.id}</div>
</div>
<div>
<span className="text-muted-foreground">Business Name:</span>
<div className="font-medium">{status.account.name || 'Not set'}</div>
</div>
<div>
<span className="text-muted-foreground">Country:</span>
<div className="font-medium">{status.account.country}</div>
</div>
<div>
<span className="text-muted-foreground">Charges Enabled:</span>
<div>
{status.account.charges_enabled ? (
<Badge variant="default">Yes</Badge>
) : (
<Badge variant="destructive">No</Badge>
)}
</div>
</div>
</div>
</CardContent>
</Card>
)}
{/* Products Status */}
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-base flex items-center gap-2">
<Package className="h-4 w-4" />
Products Status
</CardTitle>
</CardHeader>
<CardContent className="pt-0">
{stripeWorking && status.products ? (
<div className="space-y-3">
<div className="flex items-center gap-2">
<Badge variant="default">
{status.products.total} total products
</Badge>
<Badge variant="outline">
{status.products.active} active
</Badge>
</div>
{status.products.sample.length > 0 && (
<div className="space-y-2">
<p className="text-xs text-muted-foreground">Recent products:</p>
{status.products.sample.map((product) => (
<div key={product.id} className="flex items-center justify-between text-sm p-2 bg-muted rounded">
<div>
<div className="font-medium">{product.name}</div>
{product.price && (
<div className="text-xs text-muted-foreground">
${(product.price.unit_amount / 100).toFixed(2)} {product.price.currency.toUpperCase()}
</div>
)}
</div>
{product.price ? (
<Badge variant="outline">Available</Badge>
) : (
<Badge variant="destructive">No Price</Badge>
)}
</div>
))}
</div>
)}
{status.products.total === 0 && (
<div className="text-center py-4">
<Package className="h-8 w-8 text-muted-foreground mx-auto mb-2" />
<p className="text-sm text-muted-foreground">
No products found. Add some products in Stripe Dashboard.
</p>
<Button size="sm" className="mt-2" asChild>
<a href="https://dashboard.stripe.com/products/new">
<ExternalLink className="h-3 w-3 mr-1" />
Add Products
</a>
</Button>
</div>
)}
</div>
) : !stripeWorking ? (
<div className="text-center py-4">
<AlertTriangle className="h-8 w-8 text-yellow-600 mx-auto mb-2" />
<p className="text-sm text-muted-foreground">
Connect to Stripe to view product status
</p>
</div>
) : (
<div className="text-center py-4">
<RefreshCw className="h-6 w-6 animate-spin text-muted-foreground mx-auto mb-2" />
<p className="text-sm text-muted-foreground">
Loading products...
</p>
</div>
)}
</CardContent>
</Card>
{/* Payment Methods */}
{stripeWorking && status.paymentMethods && (
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-base flex items-center gap-2">
<CreditCard className="h-4 w-4" />
Payment Methods
</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<div className="flex items-center gap-2">
<Badge variant="default">
{status.paymentMethods.total} methods available
</Badge>
<div className="flex gap-1">
{status.paymentMethods.types.map((type) => (
<Badge key={type} variant="outline" className="text-xs">
{type}
</Badge>
))}
</div>
</div>
</CardContent>
</Card>
)}
{/* Quick Actions */}
{stripeWorking && (
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-base">Quick Actions</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<div className="flex gap-2 flex-wrap">
<Button size="sm" variant="outline" asChild>
<a href="https://dashboard.stripe.com/products/new">
<Package className="h-3 w-3 mr-1" />
Add Products
</a>
</Button>
<Button size="sm" variant="outline" asChild>
<a href="/products">
<ShoppingCart className="h-3 w-3 mr-1" />
View Store
</a>
</Button>
<Button size="sm" variant="outline" asChild>
<a href="/admin">
<CreditCard className="h-3 w-3 mr-1" />
Admin Panel
</a>
</Button>
<Button size="sm" variant="outline" asChild>
<a href="/stripe-setup">
<RefreshCw className="h-3 w-3 mr-1" />
Setup Guide
</a>
</Button>
</div>
</CardContent>
</Card>
)}
</div>
)
}