Rocky_Mountain_Vending/components/stripe-status.tsx

375 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>
)
}