fix: resolve container width issues, spa redirects, and appearance settings overwrite. feat: enhance order/sub details and newsletter layout
This commit is contained in:
@@ -48,6 +48,7 @@ interface Subscription {
|
||||
end_date: string | null;
|
||||
last_payment_date: string | null;
|
||||
payment_method: string;
|
||||
payment_method_title?: string;
|
||||
pause_count: number;
|
||||
failed_payment_count: number;
|
||||
cancel_reason: string | null;
|
||||
@@ -65,6 +66,7 @@ const statusColors: Record<string, string> = {
|
||||
'cancelled': 'bg-gray-100 text-gray-800',
|
||||
'expired': 'bg-red-100 text-red-800',
|
||||
'pending-cancel': 'bg-orange-100 text-orange-800',
|
||||
'draft': 'bg-gray-100 text-gray-600',
|
||||
};
|
||||
|
||||
const statusLabels: Record<string, string> = {
|
||||
@@ -74,6 +76,7 @@ const statusLabels: Record<string, string> = {
|
||||
'cancelled': __('Cancelled'),
|
||||
'expired': __('Expired'),
|
||||
'pending-cancel': __('Pending Cancel'),
|
||||
'draft': __('Draft'),
|
||||
};
|
||||
|
||||
const orderTypeLabels: Record<string, string> = {
|
||||
@@ -83,6 +86,22 @@ const orderTypeLabels: Record<string, string> = {
|
||||
'resubscribe': __('Resubscribe'),
|
||||
};
|
||||
|
||||
const formatPrice = (amount: string | number) => {
|
||||
const val = typeof amount === 'string' ? parseFloat(amount) : amount;
|
||||
if (isNaN(val)) return amount;
|
||||
|
||||
// Simple formatting using browser's locale but keeping currency from store
|
||||
try {
|
||||
return new Intl.NumberFormat(window.WNW_STORE?.locale || 'en-US', {
|
||||
style: 'currency',
|
||||
currency: window.WNW_STORE?.currency || 'USD',
|
||||
minimumFractionDigits: window.WNW_STORE?.decimals || 2,
|
||||
}).format(val);
|
||||
} catch (e) {
|
||||
return (window.WNW_STORE?.currency_symbol || '$') + val;
|
||||
}
|
||||
};
|
||||
|
||||
async function fetchSubscription(id: string) {
|
||||
const res = await fetch(`${window.WNW_API.root}/subscriptions/${id}`, {
|
||||
headers: { 'X-WP-Nonce': window.WNW_API.nonce },
|
||||
@@ -257,7 +276,7 @@ export default function SubscriptionDetail() {
|
||||
{subscription.billing_schedule}
|
||||
</p>
|
||||
<p className="text-lg font-semibold mt-1">
|
||||
{window.WNW_STORE?.currency_symbol}{subscription.recurring_amount}
|
||||
{formatPrice(subscription.recurring_amount)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -317,7 +336,7 @@ export default function SubscriptionDetail() {
|
||||
<div className="text-sm text-muted-foreground">{__('Payment Method')}</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<CreditCard className="w-4 h-4" />
|
||||
{subscription.payment_method || __('Not set')}
|
||||
{subscription.payment_method_title || subscription.payment_method || __('Not set')}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
@@ -368,29 +387,32 @@ export default function SubscriptionDetail() {
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
subscription.orders?.map((order) => (
|
||||
<TableRow key={order.id}>
|
||||
<TableCell>
|
||||
<Link
|
||||
to={`/orders/${order.order_id}`}
|
||||
className="text-primary hover:underline font-medium"
|
||||
>
|
||||
#{order.order_id}
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant="outline">
|
||||
{orderTypeLabels[order.order_type] || order.order_type}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span className="capitalize">{order.order_status?.replace('wc-', '')}</span>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{new Date(order.created_at).toLocaleDateString()}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
subscription.orders?.map((order) => {
|
||||
const rawStatus = order.order_status?.replace('wc-', '') || 'pending';
|
||||
return (
|
||||
<TableRow key={order.id}>
|
||||
<TableCell>
|
||||
<Link
|
||||
to={`/orders/${order.order_id}`}
|
||||
className="text-primary hover:underline font-medium"
|
||||
>
|
||||
#{order.order_id}
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant="outline">
|
||||
{orderTypeLabels[order.order_type] || order.order_type}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span className="capitalize">{statusLabels[rawStatus] || rawStatus}</span>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{new Date(order.created_at).toLocaleDateString()}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useNavigate, useSearchParams, Link } from 'react-router-dom';
|
||||
import { Repeat, MoreHorizontal, Play, Pause, XCircle, RefreshCw, Eye, Calendar, User, Package } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -151,6 +152,23 @@ export default function SubscriptionsIndex() {
|
||||
const total = data?.total || 0;
|
||||
const totalPages = Math.ceil(total / 20);
|
||||
|
||||
// Checkbox logic
|
||||
const [selectedIds, setSelectedIds] = React.useState<number[]>([]);
|
||||
|
||||
const toggleAll = () => {
|
||||
if (selectedIds.length === subscriptions.length) {
|
||||
setSelectedIds([]);
|
||||
} else {
|
||||
setSelectedIds(subscriptions.map(s => s.id));
|
||||
}
|
||||
};
|
||||
|
||||
const toggleRow = (id: number) => {
|
||||
setSelectedIds(prev =>
|
||||
prev.includes(id) ? prev.filter(i => i !== id) : [...prev, id]
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Header */}
|
||||
@@ -181,6 +199,13 @@ export default function SubscriptionsIndex() {
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-12 p-3">
|
||||
<Checkbox
|
||||
checked={subscriptions.length > 0 && selectedIds.length === subscriptions.length}
|
||||
onCheckedChange={toggleAll}
|
||||
aria-label={__('Select all')}
|
||||
/>
|
||||
</TableHead>
|
||||
<TableHead className="w-[80px]">{__('ID')}</TableHead>
|
||||
<TableHead>{__('Customer')}</TableHead>
|
||||
<TableHead>{__('Product')}</TableHead>
|
||||
@@ -215,7 +240,18 @@ export default function SubscriptionsIndex() {
|
||||
) : (
|
||||
subscriptions.map((sub) => (
|
||||
<TableRow key={sub.id}>
|
||||
<TableCell className="font-medium">#{sub.id}</TableCell>
|
||||
<TableCell className="p-3">
|
||||
<Checkbox
|
||||
checked={selectedIds.includes(sub.id)}
|
||||
onCheckedChange={() => toggleRow(sub.id)}
|
||||
aria-label={__('Select subscription')}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className="font-medium">
|
||||
<Link to={`/subscriptions/${sub.id}`} className="hover:underline">
|
||||
#{sub.id}
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div>
|
||||
<div className="font-medium">{sub.user_name}</div>
|
||||
|
||||
Reference in New Issue
Block a user