diff --git a/src/components/AddPaymentMethodDialog.js b/src/components/AddPaymentMethodDialog.js
new file mode 100644
index 0000000..0784c8f
--- /dev/null
+++ b/src/components/AddPaymentMethodDialog.js
@@ -0,0 +1,222 @@
+import React, { useState } from 'react';
+import { useStripe, useElements, CardElement } from '@stripe/react-stripe-js';
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogFooter,
+} from './ui/dialog';
+import { Button } from './ui/button';
+import { Checkbox } from './ui/checkbox';
+import { Label } from './ui/label';
+import { CreditCard, AlertCircle, Loader2 } from 'lucide-react';
+import { toast } from 'sonner';
+import api from '../utils/api';
+
+/**
+ * AddPaymentMethodDialog - Dialog for adding a new payment method using Stripe Elements
+ *
+ * This dialog should be wrapped in an Elements provider with a clientSecret
+ *
+ * @param {string} saveEndpoint - Optional custom API endpoint for saving (default: '/payment-methods')
+ */
+const AddPaymentMethodDialog = ({
+ open,
+ onOpenChange,
+ onSuccess,
+ clientSecret,
+ saveEndpoint = '/payment-methods',
+}) => {
+ const stripe = useStripe();
+ const elements = useElements();
+ const [loading, setLoading] = useState(false);
+ const [setAsDefault, setSetAsDefault] = useState(false);
+ const [error, setError] = useState(null);
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+
+ if (!stripe || !elements) {
+ return;
+ }
+
+ setLoading(true);
+ setError(null);
+
+ try {
+ // Get the CardElement
+ const cardElement = elements.getElement(CardElement);
+
+ if (!cardElement) {
+ setError('Card element not found');
+ toast.error('Card element not found');
+ setLoading(false);
+ return;
+ }
+
+ // Confirm the SetupIntent with the card element
+ const { error: stripeError, setupIntent } = await stripe.confirmCardSetup(
+ clientSecret,
+ {
+ payment_method: {
+ card: cardElement,
+ },
+ }
+ );
+
+ if (stripeError) {
+ setError(stripeError.message);
+ toast.error(stripeError.message);
+ setLoading(false);
+ return;
+ }
+
+ if (setupIntent.status === 'succeeded') {
+ // Save the payment method to our backend using the specified endpoint
+ await api.post(saveEndpoint, {
+ stripe_payment_method_id: setupIntent.payment_method,
+ set_as_default: setAsDefault,
+ });
+
+ toast.success('Payment method added successfully');
+ onSuccess?.();
+ onOpenChange(false);
+ } else {
+ setError(`Setup failed with status: ${setupIntent.status}`);
+ toast.error('Failed to set up payment method');
+ }
+ } catch (err) {
+ const errorMessage = err.response?.data?.detail || err.message || 'Failed to save payment method';
+ setError(errorMessage);
+ toast.error(errorMessage);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+ );
+};
+
+export default AddPaymentMethodDialog;
diff --git a/src/components/PasswordConfirmDialog.js b/src/components/PasswordConfirmDialog.js
new file mode 100644
index 0000000..48dc6ce
--- /dev/null
+++ b/src/components/PasswordConfirmDialog.js
@@ -0,0 +1,151 @@
+import React, { useState } from 'react';
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogFooter,
+} from './ui/dialog';
+import { Button } from './ui/button';
+import { Input } from './ui/input';
+import { Label } from './ui/label';
+import { Shield, Eye, EyeOff, Loader2 } from 'lucide-react';
+
+/**
+ * PasswordConfirmDialog - Dialog requiring admin password re-entry for sensitive actions
+ */
+const PasswordConfirmDialog = ({
+ open,
+ onOpenChange,
+ onConfirm,
+ title = 'Confirm Your Identity',
+ description = 'Please enter your password to proceed with this action.',
+ loading = false,
+}) => {
+ const [password, setPassword] = useState('');
+ const [showPassword, setShowPassword] = useState(false);
+ const [error, setError] = useState(null);
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ setError(null);
+
+ if (!password.trim()) {
+ setError('Password is required');
+ return;
+ }
+
+ try {
+ await onConfirm(password);
+ setPassword('');
+ } catch (err) {
+ setError(err.message || 'Invalid password');
+ }
+ };
+
+ const handleOpenChange = (isOpen) => {
+ if (!isOpen) {
+ setPassword('');
+ setError(null);
+ }
+ onOpenChange(isOpen);
+ };
+
+ return (
+
+ );
+};
+
+export default PasswordConfirmDialog;
diff --git a/src/components/PaymentMethodCard.js b/src/components/PaymentMethodCard.js
new file mode 100644
index 0000000..e45fe23
--- /dev/null
+++ b/src/components/PaymentMethodCard.js
@@ -0,0 +1,186 @@
+import React from 'react';
+import { CreditCard, Trash2, Star, Banknote, Building2, FileCheck } from 'lucide-react';
+import { Button } from './ui/button';
+
+/**
+ * Card brand icon mapping
+ */
+const getBrandIcon = (brand) => {
+ const brandLower = brand?.toLowerCase();
+ // Return text abbreviation for known brands
+ switch (brandLower) {
+ case 'visa':
+ return 'VISA';
+ case 'mastercard':
+ return 'MC';
+ case 'amex':
+ case 'american_express':
+ return 'AMEX';
+ case 'discover':
+ return 'DISC';
+ default:
+ return null;
+ }
+};
+
+/**
+ * Get icon for payment method type
+ */
+const getPaymentTypeIcon = (paymentType) => {
+ switch (paymentType) {
+ case 'cash':
+ return Banknote;
+ case 'bank_transfer':
+ return Building2;
+ case 'check':
+ return FileCheck;
+ default:
+ return CreditCard;
+ }
+};
+
+/**
+ * Format payment type for display
+ */
+const formatPaymentType = (paymentType) => {
+ switch (paymentType) {
+ case 'cash':
+ return 'Cash';
+ case 'bank_transfer':
+ return 'Bank Transfer';
+ case 'check':
+ return 'Check';
+ case 'card':
+ return 'Card';
+ default:
+ return paymentType;
+ }
+};
+
+/**
+ * PaymentMethodCard - Displays a single payment method
+ */
+const PaymentMethodCard = ({
+ method,
+ onSetDefault,
+ onDelete,
+ loading = false,
+ showActions = true,
+}) => {
+ const PaymentIcon = getPaymentTypeIcon(method.payment_type);
+ const brandAbbr = method.card_brand ? getBrandIcon(method.card_brand) : null;
+ const isExpired = method.card_exp_year && method.card_exp_month &&
+ new Date(method.card_exp_year, method.card_exp_month) < new Date();
+
+ return (
+
+
+ {/* Payment Method Icon */}
+
+
+ {/* Payment Method Details */}
+
+ {method.payment_type === 'card' ? (
+ <>
+
+ {brandAbbr && (
+
+ {brandAbbr}
+
+ )}
+
+ {method.card_brand ? method.card_brand.charAt(0).toUpperCase() + method.card_brand.slice(1) : 'Card'} •••• {method.card_last4 || '****'}
+
+ {method.is_default && (
+
+
+ Default
+
+ )}
+
+
+ {isExpired ? 'Expired' : 'Expires'} {method.card_exp_month?.toString().padStart(2, '0')}/{method.card_exp_year?.toString().slice(-2)}
+ {method.card_funding && (
+ ({method.card_funding})
+ )}
+
+ >
+ ) : (
+ <>
+
+
+ {formatPaymentType(method.payment_type)}
+
+ {method.is_default && (
+
+
+ Default
+
+ )}
+
+ {method.manual_notes && (
+
+ {method.manual_notes}
+
+ )}
+ >
+ )}
+
+
+
+ {/* Actions */}
+ {showActions && (
+
+ {!method.is_default && (
+
+ )}
+
+
+ )}
+
+ );
+};
+
+export default PaymentMethodCard;
diff --git a/src/components/PaymentMethodsSection.js b/src/components/PaymentMethodsSection.js
new file mode 100644
index 0000000..5b67c35
--- /dev/null
+++ b/src/components/PaymentMethodsSection.js
@@ -0,0 +1,309 @@
+import React, { useState, useEffect, useCallback } from 'react';
+import { loadStripe } from '@stripe/stripe-js';
+import { Elements } from '@stripe/react-stripe-js';
+import { Card } from './ui/card';
+import { Button } from './ui/button';
+import { CreditCard, Plus, Loader2, AlertCircle } from 'lucide-react';
+import { toast } from 'sonner';
+import api from '../utils/api';
+import PaymentMethodCard from './PaymentMethodCard';
+import AddPaymentMethodDialog from './AddPaymentMethodDialog';
+import ConfirmationDialog from './ConfirmationDialog';
+
+// Initialize Stripe with publishable key from environment
+const stripePromise = loadStripe(process.env.REACT_APP_STRIPE_PUBLISHABLE_KEY);
+
+/**
+ * PaymentMethodsSection - Manages user payment methods
+ *
+ * Features:
+ * - List saved payment methods
+ * - Add new payment method via Stripe SetupIntent
+ * - Set default payment method
+ * - Delete payment methods
+ */
+const PaymentMethodsSection = () => {
+ const [paymentMethods, setPaymentMethods] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [actionLoading, setActionLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ // Dialog states
+ const [addDialogOpen, setAddDialogOpen] = useState(false);
+ const [clientSecret, setClientSecret] = useState(null);
+ const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
+ const [methodToDelete, setMethodToDelete] = useState(null);
+
+ /**
+ * Fetch payment methods from API
+ */
+ const fetchPaymentMethods = useCallback(async () => {
+ try {
+ setLoading(true);
+ setError(null);
+ const response = await api.get('/payment-methods');
+ setPaymentMethods(response.data);
+ } catch (err) {
+ const errorMessage = err.response?.data?.detail || 'Failed to load payment methods';
+ setError(errorMessage);
+ console.error('Failed to fetch payment methods:', err);
+ } finally {
+ setLoading(false);
+ }
+ }, []);
+
+ useEffect(() => {
+ fetchPaymentMethods();
+ }, [fetchPaymentMethods]);
+
+ /**
+ * Create SetupIntent and open add dialog
+ */
+ const handleAddNew = async () => {
+ try {
+ setActionLoading(true);
+ const response = await api.post('/payment-methods/setup-intent');
+ setClientSecret(response.data.client_secret);
+ setAddDialogOpen(true);
+ } catch (err) {
+ const errorMessage = err.response?.data?.detail || 'Failed to initialize payment setup';
+ toast.error(errorMessage);
+ console.error('Failed to create setup intent:', err);
+ } finally {
+ setActionLoading(false);
+ }
+ };
+
+ /**
+ * Handle successful payment method addition
+ */
+ const handleAddSuccess = () => {
+ setAddDialogOpen(false);
+ setClientSecret(null);
+ fetchPaymentMethods();
+ };
+
+ /**
+ * Set a payment method as default
+ */
+ const handleSetDefault = async (methodId) => {
+ try {
+ setActionLoading(true);
+ await api.put(`/payment-methods/${methodId}/default`);
+ toast.success('Default payment method updated');
+ fetchPaymentMethods();
+ } catch (err) {
+ const errorMessage = err.response?.data?.detail || 'Failed to update default payment method';
+ toast.error(errorMessage);
+ console.error('Failed to set default:', err);
+ } finally {
+ setActionLoading(false);
+ }
+ };
+
+ /**
+ * Open delete confirmation dialog
+ */
+ const handleDeleteClick = (methodId) => {
+ setMethodToDelete(methodId);
+ setDeleteConfirmOpen(true);
+ };
+
+ /**
+ * Confirm and delete payment method
+ */
+ const handleDeleteConfirm = async () => {
+ if (!methodToDelete) return;
+
+ try {
+ setActionLoading(true);
+ await api.delete(`/payment-methods/${methodToDelete}`);
+ toast.success('Payment method removed');
+ setDeleteConfirmOpen(false);
+ setMethodToDelete(null);
+ fetchPaymentMethods();
+ } catch (err) {
+ const errorMessage = err.response?.data?.detail || 'Failed to remove payment method';
+ toast.error(errorMessage);
+ console.error('Failed to delete payment method:', err);
+ } finally {
+ setActionLoading(false);
+ }
+ };
+
+ // Stripe Elements options - simplified for CardElement
+ const elementsOptions = {
+ appearance: {
+ theme: 'stripe',
+ variables: {
+ colorPrimary: '#6b5b95',
+ colorBackground: '#ffffff',
+ colorText: '#2d2a4a',
+ colorDanger: '#ef4444',
+ fontFamily: "'Nunito Sans', sans-serif",
+ borderRadius: '12px',
+ },
+ },
+ };
+
+ return (
+ <>
+
+ {/* Header */}
+
+
+
+
+ Payment Methods
+
+
+
+
+
+ {/* Loading State */}
+ {loading && (
+
+
+
+ Loading payment methods...
+
+
+ )}
+
+ {/* Error State */}
+ {error && !loading && (
+
+
+
+ {error}
+
+
+
+ )}
+
+ {/* Payment Methods List */}
+ {!loading && !error && (
+
+ {paymentMethods.length === 0 ? (
+
+
+
+ No payment methods saved
+
+
+ Add a card to make payments easier
+
+
+
+ ) : (
+ paymentMethods.map((method) => (
+
+ ))
+ )}
+
+ )}
+
+ {/* Info Text */}
+ {!loading && paymentMethods.length > 0 && (
+
+ Your default payment method will be used for subscription renewals and donations.
+
+ )}
+
+
+ {/* Add Payment Method Dialog */}
+ {clientSecret && stripePromise && (
+
+ {
+ setAddDialogOpen(open);
+ if (!open) setClientSecret(null);
+ }}
+ onSuccess={handleAddSuccess}
+ clientSecret={clientSecret}
+ />
+
+ )}
+
+ {/* Delete Confirmation Dialog */}
+
+ >
+ );
+};
+
+export default PaymentMethodsSection;
diff --git a/src/components/admin/AdminPaymentMethodsPanel.js b/src/components/admin/AdminPaymentMethodsPanel.js
new file mode 100644
index 0000000..1376a7a
--- /dev/null
+++ b/src/components/admin/AdminPaymentMethodsPanel.js
@@ -0,0 +1,531 @@
+import React, { useState, useEffect, useCallback } from 'react';
+import { loadStripe } from '@stripe/stripe-js';
+import { Elements } from '@stripe/react-stripe-js';
+import { Card } from '../ui/card';
+import { Button } from '../ui/button';
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from '../ui/select';
+import { Textarea } from '../ui/textarea';
+import { Label } from '../ui/label';
+import {
+ CreditCard,
+ Plus,
+ Loader2,
+ AlertCircle,
+ Eye,
+ Banknote,
+ Building2,
+ FileCheck,
+ Trash2,
+ Star,
+} from 'lucide-react';
+import { toast } from 'sonner';
+import api from '../../utils/api';
+import ConfirmationDialog from '../ConfirmationDialog';
+import PasswordConfirmDialog from '../PasswordConfirmDialog';
+import AddPaymentMethodDialog from '../AddPaymentMethodDialog';
+
+// Initialize Stripe with publishable key from environment
+const stripePromise = loadStripe(process.env.REACT_APP_STRIPE_PUBLISHABLE_KEY);
+
+/**
+ * Get icon for payment method type
+ */
+const getPaymentTypeIcon = (paymentType) => {
+ switch (paymentType) {
+ case 'cash':
+ return Banknote;
+ case 'bank_transfer':
+ return Building2;
+ case 'check':
+ return FileCheck;
+ default:
+ return CreditCard;
+ }
+};
+
+/**
+ * Format payment type for display
+ */
+const formatPaymentType = (paymentType) => {
+ switch (paymentType) {
+ case 'cash':
+ return 'Cash';
+ case 'bank_transfer':
+ return 'Bank Transfer';
+ case 'check':
+ return 'Check';
+ case 'card':
+ return 'Card';
+ default:
+ return paymentType;
+ }
+};
+
+/**
+ * AdminPaymentMethodsPanel - Admin panel for managing user payment methods
+ */
+const AdminPaymentMethodsPanel = ({ userId, userName }) => {
+ const [paymentMethods, setPaymentMethods] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [actionLoading, setActionLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ // Dialog states
+ const [addCardDialogOpen, setAddCardDialogOpen] = useState(false);
+ const [addManualDialogOpen, setAddManualDialogOpen] = useState(false);
+ const [clientSecret, setClientSecret] = useState(null);
+ const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
+ const [methodToDelete, setMethodToDelete] = useState(null);
+ const [revealDialogOpen, setRevealDialogOpen] = useState(false);
+ const [revealedData, setRevealedData] = useState(null);
+
+ // Manual payment form state
+ const [manualPaymentType, setManualPaymentType] = useState('cash');
+ const [manualNotes, setManualNotes] = useState('');
+ const [manualSetDefault, setManualSetDefault] = useState(false);
+
+ /**
+ * Fetch payment methods from API
+ */
+ const fetchPaymentMethods = useCallback(async () => {
+ try {
+ setLoading(true);
+ setError(null);
+ const response = await api.get(`/admin/users/${userId}/payment-methods`);
+ setPaymentMethods(response.data);
+ } catch (err) {
+ const errorMessage = err.response?.data?.detail || 'Failed to load payment methods';
+ setError(errorMessage);
+ console.error('Failed to fetch payment methods:', err);
+ } finally {
+ setLoading(false);
+ }
+ }, [userId]);
+
+ useEffect(() => {
+ if (userId) {
+ fetchPaymentMethods();
+ }
+ }, [userId, fetchPaymentMethods]);
+
+ /**
+ * Create SetupIntent for adding a card
+ */
+ const handleAddCard = async () => {
+ try {
+ setActionLoading(true);
+ const response = await api.post(`/admin/users/${userId}/payment-methods/setup-intent`);
+ setClientSecret(response.data.client_secret);
+ setAddCardDialogOpen(true);
+ } catch (err) {
+ const errorMessage = err.response?.data?.detail || 'Failed to initialize payment setup';
+ toast.error(errorMessage);
+ console.error('Failed to create setup intent:', err);
+ } finally {
+ setActionLoading(false);
+ }
+ };
+
+ /**
+ * Handle successful card addition
+ */
+ const handleCardAddSuccess = () => {
+ setAddCardDialogOpen(false);
+ setClientSecret(null);
+ fetchPaymentMethods();
+ };
+
+ /**
+ * Save manual payment method
+ */
+ const handleSaveManualPayment = async () => {
+ try {
+ setActionLoading(true);
+ await api.post(`/admin/users/${userId}/payment-methods/manual`, {
+ payment_type: manualPaymentType,
+ manual_notes: manualNotes || null,
+ set_as_default: manualSetDefault,
+ });
+ toast.success('Manual payment method recorded');
+ setAddManualDialogOpen(false);
+ setManualPaymentType('cash');
+ setManualNotes('');
+ setManualSetDefault(false);
+ fetchPaymentMethods();
+ } catch (err) {
+ const errorMessage = err.response?.data?.detail || 'Failed to record payment method';
+ toast.error(errorMessage);
+ console.error('Failed to save manual payment:', err);
+ } finally {
+ setActionLoading(false);
+ }
+ };
+
+ /**
+ * Set a payment method as default
+ */
+ const handleSetDefault = async (methodId) => {
+ try {
+ setActionLoading(true);
+ await api.put(`/admin/users/${userId}/payment-methods/${methodId}/default`);
+ toast.success('Default payment method updated');
+ fetchPaymentMethods();
+ } catch (err) {
+ const errorMessage = err.response?.data?.detail || 'Failed to update default';
+ toast.error(errorMessage);
+ } finally {
+ setActionLoading(false);
+ }
+ };
+
+ /**
+ * Confirm and delete payment method
+ */
+ const handleDeleteConfirm = async () => {
+ if (!methodToDelete) return;
+
+ try {
+ setActionLoading(true);
+ await api.delete(`/admin/users/${userId}/payment-methods/${methodToDelete}`);
+ toast.success('Payment method removed');
+ setDeleteConfirmOpen(false);
+ setMethodToDelete(null);
+ fetchPaymentMethods();
+ } catch (err) {
+ const errorMessage = err.response?.data?.detail || 'Failed to remove payment method';
+ toast.error(errorMessage);
+ } finally {
+ setActionLoading(false);
+ }
+ };
+
+ /**
+ * Reveal sensitive payment details with password confirmation
+ */
+ const handleRevealDetails = async (password) => {
+ try {
+ setActionLoading(true);
+ const response = await api.post(`/admin/users/${userId}/payment-methods/reveal`, {
+ password,
+ });
+ setRevealedData(response.data);
+ setRevealDialogOpen(false);
+ toast.success('Sensitive details revealed');
+ } catch (err) {
+ const errorMessage = err.response?.data?.detail || 'Failed to reveal details';
+ throw new Error(errorMessage);
+ } finally {
+ setActionLoading(false);
+ }
+ };
+
+ // Stripe Elements options - simplified for CardElement
+ const elementsOptions = {
+ appearance: {
+ theme: 'stripe',
+ variables: {
+ colorPrimary: '#6b5b95',
+ colorBackground: '#ffffff',
+ colorText: '#2d2a4a',
+ fontFamily: "'Nunito Sans', sans-serif",
+ borderRadius: '12px',
+ },
+ },
+ };
+
+ return (
+ <>
+
+ {/* Header */}
+
+
+
+
+ Payment Methods
+
+
+
+
+
+
+
+
+
+ {/* Loading State */}
+ {loading && (
+
+
+
+ )}
+
+ {/* Error State */}
+ {error && !loading && (
+
+
+
{error}
+
+
+ )}
+
+ {/* Payment Methods List */}
+ {!loading && !error && (
+
+ {paymentMethods.length === 0 ? (
+
+ No payment methods on file for this user.
+
+ ) : (
+ (revealedData || paymentMethods).map((method) => {
+ const PaymentIcon = getPaymentTypeIcon(method.payment_type);
+ return (
+
+
+
+
+ {method.payment_type === 'card' ? (
+ <>
+
+
+ {method.card_brand
+ ? method.card_brand.charAt(0).toUpperCase() +
+ method.card_brand.slice(1)
+ : 'Card'}{' '}
+ •••• {method.card_last4 || '****'}
+
+ {method.is_default && (
+
+
+ Default
+
+ )}
+
+
+ Expires {method.card_exp_month?.toString().padStart(2, '0')}/
+ {method.card_exp_year?.toString().slice(-2)}
+ {revealedData && method.stripe_payment_method_id && (
+
+ {method.stripe_payment_method_id}
+
+ )}
+
+ >
+ ) : (
+ <>
+
+
+ {formatPaymentType(method.payment_type)}
+
+ {method.is_default && (
+
+
+ Default
+
+ )}
+
+ {method.manual_notes && (
+
+ {method.manual_notes}
+
+ )}
+ >
+ )}
+
+
+
+ {/* Actions */}
+
+ {!method.is_default && (
+
+ )}
+
+
+
+ );
+ })
+ )}
+
+ )}
+
+
+ {/* Add Card Dialog */}
+ {clientSecret && stripePromise && (
+
+ {
+ setAddCardDialogOpen(open);
+ if (!open) setClientSecret(null);
+ }}
+ onSuccess={handleCardAddSuccess}
+ clientSecret={clientSecret}
+ saveEndpoint={`/admin/users/${userId}/payment-methods`}
+ />
+
+ )}
+
+ {/* Add Manual Payment Method Dialog */}
+
+
+
+
+
+
+
+
+
+ }
+ confirmText="Save"
+ variant="info"
+ loading={actionLoading}
+ />
+
+ {/* Delete Confirmation Dialog */}
+
+
+ {/* Password Confirm Dialog for Reveal */}
+
+ >
+ );
+};
+
+export default AdminPaymentMethodsPanel;
diff --git a/src/pages/Profile.js b/src/pages/Profile.js
index a56965f..b60790a 100644
--- a/src/pages/Profile.js
+++ b/src/pages/Profile.js
@@ -11,6 +11,7 @@ import Navbar from '../components/Navbar';
import { User, Lock, Heart, Users, Mail, BookUser, Camera, Upload, Trash2, Eye, CreditCard, Handshake, ArrowLeft } from 'lucide-react';
import { Avatar, AvatarImage, AvatarFallback } from '../components/ui/avatar';
import ChangePasswordDialog from '../components/ChangePasswordDialog';
+import PaymentMethodsSection from '../components/PaymentMethodsSection';
import { useNavigate } from 'react-router-dom';
const Profile = () => {
@@ -265,22 +266,10 @@ const Profile = () => {
-
+
+ {/* Payment Methods Section */}
+
);
diff --git a/src/pages/admin/AdminUserView.js b/src/pages/admin/AdminUserView.js
index 2861273..a08d849 100644
--- a/src/pages/admin/AdminUserView.js
+++ b/src/pages/admin/AdminUserView.js
@@ -12,6 +12,7 @@ import ConfirmationDialog from '../../components/ConfirmationDialog';
import ChangeRoleDialog from '../../components/ChangeRoleDialog';
import StatusBadge from '../../components/StatusBadge';
import TransactionHistory from '../../components/TransactionHistory';
+import AdminPaymentMethodsPanel from '../../components/admin/AdminPaymentMethodsPanel';
const AdminUserView = () => {
const { userId } = useParams();
@@ -417,6 +418,14 @@ const AdminUserView = () => {
+ {/* Payment Methods Panel */}
+
+
{/* Additional Details */}