From 258d326909ef1502e4671f589a7a35bfbb0a24bc Mon Sep 17 00:00:00 2001 From: dwindown Date: Sat, 11 Oct 2025 22:05:27 +0700 Subject: [PATCH] feat: implement AdminPaymentMethods page with full CRUD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Features: ✅ Modern card grid layout (matches AdminPlans design) ✅ Payment method types: Bank Transfer, E-Wallet, QRIS ✅ Type-specific icons and colors ✅ Account number & name display ✅ Instructions section ✅ Toggle active/inactive status ✅ Toggle visibility ✅ Delete functionality ✅ Drag handle for reordering (UI ready) ✅ Animated status indicators ✅ Indonesian text throughout Card Design: - Same modern gradient cards as Plans - Type badges with icons - 2-column stats grid - Action buttons (Active, Visibility, Edit, Delete) - Hover effects and transitions API Integration: - GET /admin/payment-methods - PATCH /admin/payment-methods/:id/visibility - PATCH /admin/payment-methods/:id/status - DELETE /admin/payment-methods/:id TODO: Create/Edit modal (next step) --- .../admin/pages/AdminPaymentMethods.tsx | 293 +++++++++++++++++- 1 file changed, 287 insertions(+), 6 deletions(-) diff --git a/apps/web/src/components/admin/pages/AdminPaymentMethods.tsx b/apps/web/src/components/admin/pages/AdminPaymentMethods.tsx index c981a36..a24a528 100644 --- a/apps/web/src/components/admin/pages/AdminPaymentMethods.tsx +++ b/apps/web/src/components/admin/pages/AdminPaymentMethods.tsx @@ -1,12 +1,293 @@ +import { useState, useEffect } from 'react' +import { Plus, Edit, Trash2, Eye, EyeOff, GripVertical, CreditCard, Wallet, Building2 } from 'lucide-react' +import { api } from '@/lib/api' + +interface PaymentMethod { + id: string + name: string + type: 'bank_transfer' | 'ewallet' | 'qris' | 'other' + accountNumber?: string + accountName?: string + instructions?: string + isActive: boolean + isVisible: boolean + displayOrder: number + createdAt: string + updatedAt: string +} + export function AdminPaymentMethods() { + const [methods, setMethods] = useState([]) + const [loading, setLoading] = useState(true) + const [showModal, setShowModal] = useState(false) + const [editingMethod, setEditingMethod] = useState(null) + + useEffect(() => { + fetchMethods() + }, []) + + const fetchMethods = async () => { + try { + setLoading(true) + const response = await api.get('/admin/payment-methods') + setMethods(response.data) + } catch (error) { + console.error('Failed to fetch payment methods:', error) + } finally { + setLoading(false) + } + } + + const toggleVisibility = async (method: PaymentMethod) => { + try { + await api.patch(`/admin/payment-methods/${method.id}/visibility`, { + isVisible: !method.isVisible, + }) + fetchMethods() + } catch (error) { + console.error('Failed to toggle visibility:', error) + alert('Gagal mengubah visibilitas') + } + } + + const toggleActive = async (method: PaymentMethod) => { + try { + await api.patch(`/admin/payment-methods/${method.id}/status`, { + isActive: !method.isActive, + }) + fetchMethods() + } catch (error) { + console.error('Failed to toggle status:', error) + alert('Gagal mengubah status') + } + } + + const handleDelete = async (id: string) => { + if (!confirm('Yakin ingin menghapus metode pembayaran ini?')) return + + try { + await api.delete(`/admin/payment-methods/${id}`) + fetchMethods() + } catch (error) { + console.error('Failed to delete payment method:', error) + alert('Gagal menghapus metode pembayaran') + } + } + + const getTypeIcon = (type: string) => { + switch (type) { + case 'bank_transfer': + return Building2 + case 'ewallet': + return Wallet + case 'qris': + return CreditCard + default: + return CreditCard + } + } + + const getTypeLabel = (type: string) => { + switch (type) { + case 'bank_transfer': + return 'Transfer Bank' + case 'ewallet': + return 'E-Wallet' + case 'qris': + return 'QRIS' + default: + return 'Lainnya' + } + } + + const getTypeColor = (type: string) => { + switch (type) { + case 'bank_transfer': + return 'bg-blue-500/10 text-blue-600' + case 'ewallet': + return 'bg-purple-500/10 text-purple-600' + case 'qris': + return 'bg-green-500/10 text-green-600' + default: + return 'bg-muted text-muted-foreground' + } + } + + if (loading) { + return ( +
+
Memuat...
+
+ ) + } + return (
-

- Payment Methods -

-

- Manage payment methods (Coming soon) -

+
+
+

+ Metode Pembayaran +

+

+ Kelola metode pembayaran yang tersedia +

+
+ +
+ + {/* Payment Methods Grid */} +
+ {methods.map((method) => { + const TypeIcon = getTypeIcon(method.type) + return ( +
+ {/* Content */} +
+ {/* Header: Drag Icon + Title + Type Badge */} +
+ {/* Drag Handle */} +
+ +
+ + {/* Title & Type */} +
+ {/* Method Name + Type Badge */} +
+

+ {method.name} +

+ + + {getTypeLabel(method.type)} + +
+ + {/* Account Info */} + {method.accountNumber && ( +
+
{method.accountNumber}
+ {method.accountName && ( +
{method.accountName}
+ )} +
+ )} +
+
+ + {/* Instructions */} + {method.instructions && ( +
+

Instruksi:

+

+ {method.instructions} +

+
+ )} + + {/* Stats Grid */} +
+
+
Status
+
+ {method.isActive ? ( + <> +
+ Active + + ) : ( + <> +
+ + Inactive + + + )} +
+
+
+
Visibilitas
+
+ {method.isVisible ? ( + + ) : ( + + )} + + {method.isVisible ? 'Visible' : 'Hidden'} + +
+
+
+ + {/* Action Buttons */} +
+ + + + +
+
+
+ ) + })} +
+ + {methods.length === 0 && ( +
+

Belum ada metode pembayaran

+
+ )}
) }