diff --git a/admin-spa/package-lock.json b/admin-spa/package-lock.json index cc5e675..f2f83a1 100644 --- a/admin-spa/package-lock.json +++ b/admin-spa/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "dependencies": { "@radix-ui/react-accordion": "^1.2.12", + "@radix-ui/react-alert-dialog": "^1.1.15", "@radix-ui/react-avatar": "^1.1.10", "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-dialog": "^1.1.15", @@ -1191,6 +1192,34 @@ } } }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz", + "integrity": "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dialog": "1.1.15", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-arrow": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", diff --git a/admin-spa/package.json b/admin-spa/package.json index de92d7a..d849818 100644 --- a/admin-spa/package.json +++ b/admin-spa/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@radix-ui/react-accordion": "^1.2.12", + "@radix-ui/react-alert-dialog": "^1.1.15", "@radix-ui/react-avatar": "^1.1.10", "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-dialog": "^1.1.15", diff --git a/admin-spa/src/components/ui/alert-dialog.tsx b/admin-spa/src/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..fa2b442 --- /dev/null +++ b/admin-spa/src/components/ui/alert-dialog.tsx @@ -0,0 +1,139 @@ +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +const AlertDialog = AlertDialogPrimitive.Root + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger + +const AlertDialogPortal = AlertDialogPrimitive.Portal + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)) +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogHeader.displayName = "AlertDialogHeader" + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogFooter.displayName = "AlertDialogFooter" + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/admin-spa/src/routes/Settings/Payments.tsx b/admin-spa/src/routes/Settings/Payments.tsx index e4d6a92..2178ef9 100644 --- a/admin-spa/src/routes/Settings/Payments.tsx +++ b/admin-spa/src/routes/Settings/Payments.tsx @@ -9,7 +9,6 @@ import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Alert, AlertDescription } from '@/components/ui/alert'; -import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion'; import { CreditCard, Banknote, Settings, RefreshCw, ExternalLink, Loader2, AlertTriangle } from 'lucide-react'; import { toast } from 'sonner'; @@ -116,16 +115,6 @@ export default function PaymentsPage() { const manualGateways = gateways.filter((g: PaymentGateway) => g.type === 'manual'); // Combine provider and other into single "3rd party" category const thirdPartyGateways = gateways.filter((g: PaymentGateway) => g.type === 'provider' || g.type === 'other'); - - // Group third party gateways by provider (title) - const gatewaysByProvider = thirdPartyGateways.reduce((acc: Record, gateway: PaymentGateway) => { - const provider = gateway.title || 'Other'; - if (!acc[provider]) { - acc[provider] = []; - } - acc[provider].push(gateway); - return acc; - }, {}); if (isLoading) { return ( @@ -208,87 +197,66 @@ export default function PaymentsPage() { )} - {/* 3rd Party Payment Methods - Grouped by provider */} - {Object.keys(gatewaysByProvider).length > 0 && ( + {/* Online Payment Methods - Flat list */} + {thirdPartyGateways.length > 0 && ( - - {(Object.entries(gatewaysByProvider) as [string, PaymentGateway[]][]).map(([provider, providerGateways]) => ( - - -
-
- +
+ {thirdPartyGateways.map((gateway: PaymentGateway) => ( +
+
+
+
+
-
-

{provider}

-

- {providerGateways.length} payment {providerGateways.length === 1 ? 'method' : 'methods'} -

-
-
- - -
- {providerGateways.map((gateway: PaymentGateway) => ( -
-
-
-
- -
-
-
-

- {gateway.method_title} -

-
- {gateway.method_description && ( -

- {gateway.method_description} -

- )} - {!gateway.requirements.met && ( - - - - Requirements not met: {gateway.requirements.missing.join(', ')} - - - )} -
-
-
- {gateway.enabled && ( - - )} - handleToggle(gateway.id, checked)} - disabled={!gateway.requirements.met || togglingGateway === gateway.id} - /> -
-
+
+
+

+ {gateway.method_title || gateway.title} +

- ))} + {gateway.method_description && ( +

+ {gateway.method_description} +

+ )} + {!gateway.requirements.met && ( + + + + Requirements not met: {gateway.requirements.missing.join(', ')} + + + )} +
- - +
+ {gateway.enabled && ( + + )} + handleToggle(gateway.id, checked)} + disabled={!gateway.requirements.met || togglingGateway === gateway.id} + /> +
+
+
))} - +
)}