feat: Mobile improvements + simplify payment categories

Mobile Improvements:
1. Modal footer buttons now stack vertically on mobile
   - Order: Save Settings (primary) -> View in WooCommerce -> Cancel
   - Full width buttons on mobile for easier tapping
   - Responsive padding: px-4 on mobile, px-6 on desktop

2. Refresh button moved inline with title
   - Added action prop to SettingsLayout
   - Refresh button now appears next to Payments title
   - Cleaner, more compact layout

Payment Categories Simplified:
3. Removed Payment Providers section
   - PayPal, Stripe are also 3rd party, not different
   - Confusing to separate providers from other gateways
   - All non-manual gateways now in single category

4. Renamed to Online Payment Methods
   - Was: Manual + Payment Providers + 3rd Party
   - Now: Manual + Online Payment Methods
   - Clearer distinction: offline vs online payments

5. Unified styling for all online gateways
   - Same card style as manual methods
   - Status badges (Enabled/Disabled)
   - Requirements alerts
   - Manage button always visible

Mobile UX:
- Footer buttons: flex-col on mobile, flex-row on desktop
- Proper button ordering with CSS order utilities
- Responsive spacing and padding
- Touch-friendly button sizes

Files Modified:
- Payments.tsx: Mobile footer + simplified categories
- SettingsLayout.tsx: Added action prop for header actions

Result:
 Better mobile experience
 Clearer payment method organization
 Consistent styling across all gateways
This commit is contained in:
dwindown
2025-11-06 00:20:38 +07:00
parent 91449bec60
commit 1f88120c9d
2 changed files with 37 additions and 87 deletions

View File

@@ -113,8 +113,8 @@ export default function PaymentsPage() {
// Categorize gateways
const manualGateways = gateways.filter((g: PaymentGateway) => g.type === 'manual');
const providerGateways = gateways.filter((g: PaymentGateway) => g.type === 'provider');
const otherGateways = gateways.filter((g: PaymentGateway) => g.type === 'other');
// Combine provider and other into single "3rd party" category
const thirdPartyGateways = gateways.filter((g: PaymentGateway) => g.type === 'provider' || g.type === 'other');
if (isLoading) {
return (
@@ -128,9 +128,10 @@ export default function PaymentsPage() {
return (
<>
<SettingsLayout title="Payments" description="Manage how you get paid">
{/* Refresh button */}
<div className="flex justify-end mb-4">
<SettingsLayout
title="Payments"
description="Manage how you get paid"
action={
<Button
variant="outline"
size="sm"
@@ -140,7 +141,8 @@ export default function PaymentsPage() {
<RefreshCw className="h-4 w-4 mr-2" />
Refresh
</Button>
</div>
}
>
{/* Manual Payment Methods - First priority */}
<SettingsCard
@@ -195,27 +197,14 @@ export default function PaymentsPage() {
)}
</SettingsCard>
{/* Payment Providers - Second priority */}
<SettingsCard
title="Payment Providers"
description="Accept credit cards and digital wallets"
>
{providerGateways.length === 0 ? (
<p className="text-sm text-muted-foreground">
No payment providers installed.{' '}
<a
href="https://woocommerce.com/product-category/woocommerce-extensions/payment-gateways/"
target="_blank"
rel="noopener noreferrer"
className="underline inline-flex items-center gap-1"
>
Browse payment gateways
<ExternalLink className="h-3 w-3" />
</a>
</p>
) : (
{/* 3rd Party Payment Methods - All online payment gateways */}
{thirdPartyGateways.length > 0 && (
<SettingsCard
title="Online Payment Methods"
description="Accept credit cards, digital wallets, and other online payments"
>
<div className="space-y-4">
{providerGateways.map((gateway: PaymentGateway) => (
{thirdPartyGateways.map((gateway: PaymentGateway) => (
<div
key={gateway.id}
className="border rounded-lg p-4 hover:border-primary/50 transition-colors"
@@ -236,9 +225,11 @@ export default function PaymentsPage() {
<Badge variant="secondary"> Disabled</Badge>
)}
</div>
<p className="text-sm text-muted-foreground mb-2">
{gateway.description}
</p>
{gateway.method_description && (
<p className="text-sm text-muted-foreground mb-2">
{gateway.method_description}
</p>
)}
{!gateway.requirements.met && (
<Alert variant="destructive" className="mt-2">
<AlertTriangle className="h-4 w-4" />
@@ -270,57 +261,6 @@ export default function PaymentsPage() {
</div>
))}
</div>
)}
</SettingsCard>
{/* 3rd Party Gateways */}
{otherGateways.length > 0 && (
<SettingsCard
title="3rd Party Payment Methods"
description="Additional payment gateways from plugins"
>
<div className="space-y-4">
{otherGateways.map((gateway: PaymentGateway) => (
<div
key={gateway.id}
className="border rounded-lg p-4"
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<div className="p-2 bg-muted rounded-lg">
<CreditCard className="h-5 w-5 text-muted-foreground" />
</div>
<div>
<h3 className="font-medium">{gateway.method_title || gateway.title}</h3>
{gateway.method_description && (
<p className="text-sm text-muted-foreground mt-1">
{gateway.method_description}
</p>
)}
</div>
</div>
<div className="flex items-center gap-2">
{gateway.enabled && (
<Button
variant="ghost"
size="sm"
onClick={() => handleManageGateway(gateway)}
>
<Settings className="h-4 w-4" />
</Button>
)}
<ToggleField
id={gateway.id}
label=""
checked={gateway.enabled}
onCheckedChange={(checked) => handleToggle(gateway.id, checked)}
disabled={togglingGateway === gateway.id}
/>
</div>
</div>
</div>
))}
</div>
</SettingsCard>
)}
</SettingsLayout>
@@ -332,7 +272,7 @@ export default function PaymentsPage() {
<DialogHeader className="px-6 pt-6 pb-4 border-b shrink-0">
<DialogTitle>{selectedGateway.title} Settings</DialogTitle>
</DialogHeader>
<div className="flex-1 overflow-y-auto px-6 py-4 min-h-0">
<div className="flex-1 overflow-y-auto p-6 min-h-0">
<GenericGatewayForm
gateway={selectedGateway}
onSave={handleSaveGateway}
@@ -341,20 +281,22 @@ export default function PaymentsPage() {
/>
</div>
{/* Footer outside scrollable area */}
<div className="border-t px-6 py-4 flex items-center justify-between shrink-0 bg-background">
<div className="border-t px-4 sm:px-6 py-3 sm:py-4 flex flex-col sm:flex-row items-stretch sm:items-center gap-2 sm:gap-0 sm:justify-between shrink-0 bg-background sm:rounded-b-lg">
<Button
type="button"
variant="outline"
onClick={() => setIsModalOpen(false)}
disabled={saveMutation.isPending}
className="order-3 sm:order-1"
>
Cancel
</Button>
<div className="flex items-center gap-2">
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-2 order-1 sm:order-2">
<Button
type="button"
variant="ghost"
asChild
className="justify-center"
>
<a
href={selectedGateway.wc_settings_url}
@@ -372,6 +314,7 @@ export default function PaymentsPage() {
if (form) form.requestSubmit();
}}
disabled={saveMutation.isPending}
className="order-1 sm:order-2"
>
{saveMutation.isPending ? 'Saving...' : 'Save Settings'}
</Button>