fix: Select defaults + confirm responsive pattern + convert to AlertDialog

1. Fixed Select Field Default Value 
   Problem: Select shows empty even with default/saved value
   Solution: Ensure select always has value

   const selectValue = (value || field.value || field.default) as string;
   <Select value={selectValue}>

   Priority: current > saved > default
   Result:  Select always shows correct value

2. Confirmed Responsive Pattern 
   ResponsiveDialog already working correctly:
   - Desktop (≥768px): Dialog component
   - Mobile (<768px): Drawer component
   - useMediaQuery hook detects screen size

    No changes needed - already correct!

3. Converted to AlertDialog 

   A. Orders/Detail.tsx - Retry Payment
      - Was: Dialog (can dismiss by clicking outside)
      - Now: AlertDialog (must choose action)
      - Better for critical payment retry action

   B. Orders/index.tsx - Delete Orders
      - Was: Dialog (can dismiss by clicking outside)
      - Now: AlertDialog (must choose action)
      - Better for destructive delete action

   Benefits:
   -  No close button (forces decision)
   -  Can't dismiss by clicking outside
   -  User must explicitly choose Cancel or Confirm
   -  Better UX for critical/destructive actions

Component Usage Summary:
- Dialog: Forms, settings, content display
- Drawer: Mobile bottom sheet (auto via ResponsiveDialog)
- AlertDialog: Confirmations, destructive actions

Files Modified:
- GenericGatewayForm.tsx: Select default value fix
- Orders/Detail.tsx: Dialog → AlertDialog
- Orders/index.tsx: Dialog → AlertDialog
This commit is contained in:
dwindown
2025-11-06 10:28:04 +07:00
parent 108155db50
commit f9161b49f4
3 changed files with 54 additions and 35 deletions

View File

@@ -160,6 +160,8 @@ export function GenericGatewayForm({ gateway, onSave, onCancel, hideFooter = fal
); );
case 'select': case 'select':
// Ensure select has a value - use current value, saved value, or default
const selectValue = (value || field.value || field.default) as string;
return ( return (
<div key={field.id} className="space-y-2"> <div key={field.id} className="space-y-2">
<Label htmlFor={field.id}> <Label htmlFor={field.id}>
@@ -173,7 +175,7 @@ export function GenericGatewayForm({ gateway, onSave, onCancel, hideFooter = fal
/> />
)} )}
<Select <Select
value={value as string} value={selectValue}
onValueChange={(val) => handleFieldChange(field.id, val)} onValueChange={(val) => handleFieldChange(field.id, val)}
> >
<SelectTrigger id={field.id}> <SelectTrigger id={field.id}>

View File

@@ -6,7 +6,16 @@ import { formatRelativeOrDate } from '@/lib/dates';
import { formatMoney } from '@/lib/currency'; import { formatMoney } from '@/lib/currency';
import { ArrowLeft, Printer, ExternalLink, Loader2, Ticket, FileText, Pencil, RefreshCw } from 'lucide-react'; import { ArrowLeft, Printer, ExternalLink, Loader2, Ticket, FileText, Pencil, RefreshCw } from 'lucide-react';
import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from '@/components/ui/select'; import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from '@/components/ui/select';
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle
} from '@/components/ui/alert-dialog';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { showErrorToast, showSuccessToast, getPageLoadErrorMessage } from '@/lib/errorHandling'; import { showErrorToast, showSuccessToast, getPageLoadErrorMessage } from '@/lib/errorHandling';
import { ErrorCard } from '@/components/ErrorCard'; import { ErrorCard } from '@/components/ErrorCard';
@@ -254,33 +263,33 @@ export default function OrderShow() {
{__('Retry Payment')} {__('Retry Payment')}
</button> </button>
<Dialog open={showRetryDialog} onOpenChange={setShowRetryDialog}> <AlertDialog open={showRetryDialog} onOpenChange={setShowRetryDialog}>
<DialogContent> <AlertDialogContent>
<DialogHeader> <AlertDialogHeader>
<DialogTitle>{__('Retry Payment')}</DialogTitle> <AlertDialogTitle>{__('Retry Payment')}</AlertDialogTitle>
<DialogDescription> <AlertDialogDescription>
{__('Are you sure you want to retry payment processing for this order?')} {__('Are you sure you want to retry payment processing for this order?')}
<br /> <br />
<span className="text-amber-600 font-medium"> <span className="text-amber-600 font-medium">
{__('This will create a new payment transaction.')} {__('This will create a new payment transaction.')}
</span> </span>
</DialogDescription> </AlertDialogDescription>
</DialogHeader> </AlertDialogHeader>
<DialogFooter> <AlertDialogFooter>
<Button variant="outline" onClick={() => setShowRetryDialog(false)}> <AlertDialogCancel onClick={() => setShowRetryDialog(false)}>
{__('Cancel')} {__('Cancel')}
</Button> </AlertDialogCancel>
<Button onClick={confirmRetryPayment} disabled={retryPaymentMutation.isPending}> <AlertDialogAction onClick={confirmRetryPayment} disabled={retryPaymentMutation.isPending}>
{retryPaymentMutation.isPending ? ( {retryPaymentMutation.isPending ? (
<Loader2 className="w-4 h-4 animate-spin mr-2" /> <Loader2 className="w-4 h-4 animate-spin mr-2" />
) : ( ) : (
<RefreshCw className="w-4 h-4 mr-2" /> <RefreshCw className="w-4 h-4 mr-2" />
)} )}
{__('Retry Payment')} {__('Retry Payment')}
</Button> </AlertDialogAction>
</DialogFooter> </AlertDialogFooter>
</DialogContent> </AlertDialogContent>
</Dialog> </AlertDialog>
</> </>
)} )}
</div> </div>

View File

@@ -6,7 +6,16 @@ import { ErrorCard } from '@/components/ErrorCard';
import { getPageLoadErrorMessage } from '@/lib/errorHandling'; import { getPageLoadErrorMessage } from '@/lib/errorHandling';
import { __ } from '@/lib/i18n'; import { __ } from '@/lib/i18n';
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card'; import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card';
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle
} from '@/components/ui/alert-dialog';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox'; import { Checkbox } from '@/components/ui/checkbox';
import { toast } from 'sonner'; import { toast } from 'sonner';
@@ -435,34 +444,33 @@ export default function Orders() {
</div> </div>
{/* Delete Confirmation Dialog */} {/* Delete Confirmation Dialog */}
<Dialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}> <AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
<DialogContent> <AlertDialogContent>
<DialogHeader> <AlertDialogHeader>
<DialogTitle>{__('Delete Orders')}</DialogTitle> <AlertDialogTitle>{__('Delete Orders')}</AlertDialogTitle>
<DialogDescription> <AlertDialogDescription>
{__('Are you sure you want to delete')} {selectedIds.length} {selectedIds.length === 1 ? __('order') : __('orders')}? {__('Are you sure you want to delete')} {selectedIds.length} {selectedIds.length === 1 ? __('order') : __('orders')}?
<br /> <br />
<span className="text-red-600 font-medium">{__('This action cannot be undone.')}</span> <span className="text-red-600 font-medium">{__('This action cannot be undone.')}</span>
</DialogDescription> </AlertDialogDescription>
</DialogHeader> </AlertDialogHeader>
<DialogFooter> <AlertDialogFooter>
<Button <AlertDialogCancel
variant="outline"
onClick={() => setShowDeleteDialog(false)} onClick={() => setShowDeleteDialog(false)}
disabled={deleteMutation.isPending} disabled={deleteMutation.isPending}
> >
{__('Cancel')} {__('Cancel')}
</Button> </AlertDialogCancel>
<Button <AlertDialogAction
variant="destructive"
onClick={confirmDelete} onClick={confirmDelete}
disabled={deleteMutation.isPending} disabled={deleteMutation.isPending}
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
> >
{deleteMutation.isPending ? __('Deleting...') : __('Delete')} {deleteMutation.isPending ? __('Deleting...') : __('Delete')}
</Button> </AlertDialogAction>
</DialogFooter> </AlertDialogFooter>
</DialogContent> </AlertDialogContent>
</Dialog> </AlertDialog>
</div> </div>
); );
} }