107 lines
4.5 KiB
TypeScript
107 lines
4.5 KiB
TypeScript
import React from 'react'
|
||
import { useMyAboStatus } from '../hooks/getAbo'
|
||
|
||
type Props = {
|
||
onAboChange?: (aboId: string | number | null) => void
|
||
}
|
||
|
||
export default function UserAbo({ onAboChange }: Props) {
|
||
const { hasAbo, abonement, loading, error } = useMyAboStatus()
|
||
|
||
React.useEffect(() => {
|
||
if (!onAboChange) return
|
||
onAboChange(abonement?.id ?? null)
|
||
}, [abonement?.id, onAboChange])
|
||
|
||
if (loading) {
|
||
return (
|
||
<section className="space-y-4">
|
||
<h2 className="text-lg font-semibold text-gray-900">My Subscriptions</h2>
|
||
<div className="rounded-md border border-white/60 bg-white/70 backdrop-blur-md p-4 text-sm text-gray-600 shadow-lg">
|
||
Loading subscriptions…
|
||
</div>
|
||
</section>
|
||
)
|
||
}
|
||
|
||
if (error) {
|
||
return (
|
||
<section className="space-y-4">
|
||
<h2 className="text-lg font-semibold text-gray-900">My Subscriptions</h2>
|
||
<div className="rounded-md border border-red-200 bg-red-50/80 backdrop-blur-md p-4 text-sm text-red-700">
|
||
{error}
|
||
</div>
|
||
</section>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<section className="space-y-4">
|
||
<h2 className="text-lg font-semibold text-gray-900">My Subscription</h2>
|
||
{(!hasAbo || !abonement) ? (
|
||
<div className="rounded-md border border-white/60 bg-white/70 backdrop-blur-md p-4 text-sm text-gray-600 shadow-lg">
|
||
You currently don’t have an active subscription.
|
||
</div>
|
||
) : (
|
||
<div className="grid gap-3 sm:gap-4">
|
||
{(() => {
|
||
const status = (abonement.status || 'active') as 'active' | 'paused' | 'canceled'
|
||
const nextBilling = abonement.nextBillingAt ? new Date(abonement.nextBillingAt).toLocaleDateString() : '—'
|
||
const started = abonement.startedAt ? new Date(abonement.startedAt).toLocaleDateString() : '—'
|
||
const coffees = (abonement.pack_breakdown || abonement.items || []).map((it, i) => (
|
||
<span
|
||
key={i}
|
||
className="inline-flex items-center gap-1.5 rounded-full bg-white text-[#1C2B4A] px-3 py-1.5 text-xs font-medium border border-gray-200 shadow-sm ring-1 ring-gray-100 hover:shadow-md hover:ring-gray-200 transition"
|
||
>
|
||
{/* coffee name */}
|
||
<span className="truncate max-w-[14rem]">{it.coffeeName || `Coffee #${it.coffeeId}`}</span>
|
||
{/* packs pill — CHANGED COLORS TO MATCH ACTIVE BADGE */}
|
||
<span className="inline-flex items-center rounded-full bg-green-100 text-green-800 px-2 py-0.5 text-[10px] font-semibold border border-green-200">
|
||
{it.quantity} packs
|
||
</span>
|
||
</span>
|
||
))
|
||
return (
|
||
<div key={abonement.id} className="rounded-lg border border-white/60 bg-white/70 backdrop-blur-md p-4 shadow-lg">
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<p className="text-sm font-medium text-gray-900">{abonement.name || 'Coffee Subscription'}</p>
|
||
<p className="text-xs text-gray-600">
|
||
Next billing: {nextBilling}
|
||
{' • '}Frequency: {abonement.frequency ?? '—'}
|
||
{' • '}Country: {(abonement.country ?? '').toUpperCase() || '—'}
|
||
{' • '}Started: {started}
|
||
</p>
|
||
</div>
|
||
<span
|
||
className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${
|
||
status === 'active'
|
||
? 'bg-green-100 text-green-800'
|
||
: status === 'paused'
|
||
? 'bg-amber-100 text-amber-800'
|
||
: 'bg-gray-100 text-gray-700'
|
||
}`}
|
||
>
|
||
{status.charAt(0).toUpperCase() + status.slice(1)}
|
||
</span>
|
||
</div>
|
||
<div className="mt-3">
|
||
<p className="text-xs font-semibold text-gray-700 mb-1">Coffees</p>
|
||
<div className="flex flex-wrap gap-2">
|
||
{coffees}
|
||
</div>
|
||
</div>
|
||
<div className="mt-3 flex gap-2">
|
||
<button className="rounded-md border border-gray-300 px-3 py-1.5 text-xs text-gray-700 hover:bg-gray-50">
|
||
Current plan
|
||
</button>
|
||
</div>
|
||
</div>
|
||
)
|
||
})()}
|
||
</div>
|
||
)}
|
||
</section>
|
||
)
|
||
}
|