feat: Add support for more WooCommerce field types + prepare for sorting

1. Added Support for More Field Types 

   New field types:
   - 'title': Heading/separator (renders as h3 with border)
   - 'multiselect': Multiple select dropdown
   - 'account': Bank account repeater (BACS)

   Total supported: text, password, checkbox, select, textarea,
                    number, email, url, account, title, multiselect

2. Improved Account Field Handling 

   Problem: WooCommerce might return serialized PHP or JSON string
   Solution: Parse string values before rendering

   Handles:
   - JSON string: JSON.parse()
   - Array: Use directly
   - Empty/invalid: Default to []

   This ensures bank accounts display correctly even if
   backend returns different formats.

3. Added Title Field Support 

   Renders as section heading:
   ┌─────────────────────────────┐
   │ Account Details             │ ← Title
   │ Configure your bank...      │ ← Description
   ├─────────────────────────────┤
   │ [Account fields below]      │
   └─────────────────────────────┘

4. Installed DnD Kit for Sorting 

   Packages installed:
   - @dnd-kit/core
   - @dnd-kit/sortable
   - @dnd-kit/utilities

   Prepared components:
   - SortableGatewayItem wrapper
   - Drag handle with GripVertical icon
   - DnD sensors and context

   Next: Wire up sorting logic and save order

Why This Matters:
 Bank account repeater will now work for BACS
 Supports all common WooCommerce field types
 Handles different data formats from backend
 Better organized settings with title separators
 Ready for drag-and-drop sorting

Files Modified:
- GenericGatewayForm.tsx: New field types + parsing
- Payments.tsx: DnD imports + sortable component
- package.json: DnD kit dependencies
This commit is contained in:
dwindown
2025-11-06 12:44:13 +07:00
parent 2008f2f141
commit b221fe8b59
4 changed files with 130 additions and 3 deletions

View File

@@ -10,9 +10,12 @@ import { Badge } from '@/components/ui/badge';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Drawer, DrawerContent, DrawerHeader, DrawerTitle } from '@/components/ui/drawer';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { CreditCard, Banknote, Settings, RefreshCw, ExternalLink, Loader2, AlertTriangle } from 'lucide-react';
import { CreditCard, Banknote, Settings, RefreshCw, ExternalLink, Loader2, AlertTriangle, GripVertical } from 'lucide-react';
import { toast } from 'sonner';
import { useMediaQuery } from '@/hooks/use-media-query';
import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, DragEndEvent } from '@dnd-kit/core';
import { arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy, useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
interface GatewayField {
id: string;
@@ -52,13 +55,52 @@ interface PaymentGateway {
wc_settings_url: string;
}
// Sortable Gateway Item Component
function SortableGatewayItem({ gateway, children }: { gateway: PaymentGateway; children: React.ReactNode }) {
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging,
} = useSortable({ id: gateway.id });
const style = {
transform: CSS.Transform.toString(transform),
transition,
opacity: isDragging ? 0.5 : 1,
};
return (
<div ref={setNodeRef} style={style} className="relative">
<div className="absolute left-2 top-1/2 -translate-y-1/2 cursor-grab active:cursor-grabbing" {...attributes} {...listeners}>
<GripVertical className="h-5 w-5 text-muted-foreground" />
</div>
<div className="pl-8">
{children}
</div>
</div>
);
}
export default function PaymentsPage() {
const queryClient = useQueryClient();
const [selectedGateway, setSelectedGateway] = useState<PaymentGateway | null>(null);
const [isModalOpen, setIsModalOpen] = useState(false);
const [togglingGateway, setTogglingGateway] = useState<string | null>(null);
const [manualOrder, setManualOrder] = useState<string[]>([]);
const [onlineOrder, setOnlineOrder] = useState<string[]>([]);
const isDesktop = useMediaQuery("(min-width: 768px)");
// DnD sensors
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
})
);
// Fetch all payment gateways
const { data: gateways = [], isLoading, refetch } = useQuery({
queryKey: ['payment-gateways'],