From 27d5c48805c9bd029d14ec65ed1a18f251d44495 Mon Sep 17 00:00:00 2001 From: kayela Date: Thu, 29 Jan 2026 18:36:13 -0600 Subject: [PATCH] Componentized subscription table --- src/components/admin/SubscriptionsTable.jsx | 319 ++++++++++++++++++++ src/pages/admin/AdminSubscriptions.js | 291 ++---------------- 2 files changed, 337 insertions(+), 273 deletions(-) create mode 100644 src/components/admin/SubscriptionsTable.jsx diff --git a/src/components/admin/SubscriptionsTable.jsx b/src/components/admin/SubscriptionsTable.jsx new file mode 100644 index 0000000..e3881fe --- /dev/null +++ b/src/components/admin/SubscriptionsTable.jsx @@ -0,0 +1,319 @@ +import React from 'react'; +import { Button } from '../ui/button'; +import StatusBadge from '../StatusBadge'; +import { + ChevronDown, + ChevronUp, + Edit, + XCircle, + CreditCard, + Info, + ExternalLink, + Copy +} from 'lucide-react'; + +const HEADER_CELLS = [ + { label: 'Member', align: 'text-left' }, + { label: 'Plan', align: 'text-left' }, + { label: 'Status', align: 'text-left' }, + { label: 'Period', align: 'text-left' }, + { label: 'Base Fee', align: 'text-right' }, + { label: 'Donation', align: 'text-right' }, + { label: 'Total', align: 'text-right' }, + { label: 'Details', align: 'text-center' }, + { label: 'Actions', align: 'text-center' } +]; + +const HeaderCell = ({ align, children }) => ( + + {children} + +); + +const SubscriptionRow = ({ + sub, + isExpanded, + onToggle, + onEdit, + onCancel, + hasPermission, + formatDate, + formatDateTime, + formatPrice, + copyToClipboard +}) => ( + <> + + +
+ {sub.user.first_name} {sub.user.last_name} +
+
+ {sub.user.email} +
+ + +
+ {sub.plan.name} +
+
+ {sub.plan.billing_cycle} +
+ + + + + +
+
{formatDate(sub.start_date)}
+
to {formatDate(sub.end_date)}
+
+ + + {formatPrice(sub.base_subscription_cents || 0)} + + + {formatPrice(sub.donation_cents || 0)} + + + {formatPrice(sub.amount_paid_cents || 0)} + + + + + +
+ {hasPermission('subscriptions.edit') && ( + + )} + {sub.status === 'active' && hasPermission('subscriptions.cancel') && ( + + )} +
+ + + + {isExpanded && ( + + +
+

+ Transaction Details +

+
+
+
+ + Payment Information +
+
+ {sub.payment_completed_at && ( +
+ Payment Date: + {formatDateTime(sub.payment_completed_at)} +
+ )} + {sub.payment_method && ( +
+ Payment Method: + {sub.payment_method} +
+ )} + {sub.card_brand && sub.card_last4 && ( +
+ Card: + {sub.card_brand} ****{sub.card_last4} +
+ )} +
+
+ +
+
+ + Stripe Transaction IDs +
+
+ {sub.stripe_payment_intent_id && ( +
+ Payment Intent: +
+ + {sub.stripe_payment_intent_id.substring(0, 20)}... + + +
+
+ )} + {sub.stripe_charge_id && ( +
+ Charge ID: +
+ + {sub.stripe_charge_id.substring(0, 20)}... + + +
+
+ )} + {sub.stripe_subscription_id && ( +
+ Subscription ID: +
+ + {sub.stripe_subscription_id.substring(0, 20)}... + + +
+
+ )} + {sub.stripe_invoice_id && ( +
+ Invoice ID: +
+ + {sub.stripe_invoice_id.substring(0, 20)}... + + +
+
+ )} + {sub.stripe_customer_id && ( +
+ Customer ID: +
+ + {sub.stripe_customer_id.substring(0, 20)}... + + +
+
+ )} + {sub.stripe_receipt_url && ( +
+ Receipt: + +
+ )} +
+
+
+
+ + + )} + +); + +const SubscriptionsTable = ({ + subscriptions, + expandedRows, + onToggleRowExpansion, + onEdit, + onCancel, + hasPermission, + formatDate, + formatDateTime, + formatPrice, + copyToClipboard +}) => ( + + + + {HEADER_CELLS.map((cell) => ( + + {cell.label} + + ))} + + + + {subscriptions.length > 0 ? ( + subscriptions.map((sub) => ( + onToggleRowExpansion(sub.id)} + onEdit={onEdit} + onCancel={onCancel} + hasPermission={hasPermission} + formatDate={formatDate} + formatDateTime={formatDateTime} + formatPrice={formatPrice} + copyToClipboard={copyToClipboard} + /> + )) + ) : ( + + + + )} + +
+ No subscriptions found +
+); + +export default SubscriptionsTable; diff --git a/src/pages/admin/AdminSubscriptions.js b/src/pages/admin/AdminSubscriptions.js index 7915282..f5e5756 100644 --- a/src/pages/admin/AdminSubscriptions.js +++ b/src/pages/admin/AdminSubscriptions.js @@ -35,11 +35,14 @@ import { FileDown, AlertTriangle, Info, + Repeat, ChevronDown, ChevronUp, + + + ExternalLink, - Copy, - Repeat + Copy } from 'lucide-react'; import { DropdownMenu, @@ -49,6 +52,7 @@ import { } from '../../components/ui/dropdown-menu'; import StatusBadge from '@/components/StatusBadge'; import CreateSubscriptionDialog from '@/components/CreateSubscriptionDialog'; +import SubscriptionsTable from '@/components/admin/SubscriptionsTable'; const AdminSubscriptions = () => { const { hasPermission } = useAuth(); @@ -590,277 +594,18 @@ Proceed with activation?`; {/* Desktop Table View */}
- - - - - - - - - - - - - - - - {filteredSubscriptions.length > 0 ? ( - filteredSubscriptions.map((sub) => { - const isExpanded = expandedRows.has(sub.id); - return ( - - - - - - - - - - - - - {/* Expandable Details Row */} - {isExpanded && ( - - - - )} - - ); - }) - ) : ( - - - - )} - -
- Member - - Plan - - Status - - Period - - Base Fee - - Donation - - Total - - Details - - Actions -
-
- {sub.user.first_name} {sub.user.last_name} -
-
- {sub.user.email} -
-
-
- {sub.plan.name} -
-
- {sub.plan.billing_cycle} -
-
- - - -
-
{formatDate(sub.start_date)}
-
to {formatDate(sub.end_date)}
-
-
- {formatPrice(sub.base_subscription_cents || 0)} - - {formatPrice(sub.donation_cents || 0)} - - {formatPrice(sub.amount_paid_cents || 0)} - - - -
- {hasPermission('subscriptions.edit') && ( - - )} - {sub.status === 'active' && hasPermission('subscriptions.cancel') && ( - - )} -
-
-
-

- Transaction Details -

-
- {/* Payment Information */} -
-
- - Payment Information -
-
- {sub.payment_completed_at && ( -
- Payment Date: - {formatDateTime(sub.payment_completed_at)} -
- )} - {sub.payment_method && ( -
- Payment Method: - {sub.payment_method} -
- )} - {sub.card_brand && sub.card_last4 && ( -
- Card: - {sub.card_brand} ****{sub.card_last4} -
- )} -
-
- - {/* Stripe Transaction IDs */} -
-
- - Stripe Transaction IDs -
-
- {sub.stripe_payment_intent_id && ( -
- Payment Intent: -
- - {sub.stripe_payment_intent_id.substring(0, 20)}... - - -
-
- )} - {sub.stripe_charge_id && ( -
- Charge ID: -
- - {sub.stripe_charge_id.substring(0, 20)}... - - -
-
- )} - {sub.stripe_subscription_id && ( -
- Subscription ID: -
- - {sub.stripe_subscription_id.substring(0, 20)}... - - -
-
- )} - {sub.stripe_invoice_id && ( -
- Invoice ID: -
- - {sub.stripe_invoice_id.substring(0, 20)}... - - -
-
- )} - {sub.stripe_customer_id && ( -
- Customer ID: -
- - {sub.stripe_customer_id.substring(0, 20)}... - - -
-
- )} - {sub.stripe_receipt_url && ( -
- Receipt: - -
- )} -
-
-
-
-
- No subscriptions found -
+