Files
caddy-proxy-manager/src/components/ui/AppDialog.tsx
fuomag9 9c60d11c2c feat: improve UI contrast, dark mode, dialog sizing, color coherence, and add table sorting
- Fix dialog scrollability (flex layout + max-h-[90dvh]) and increase L4 dialog to lg width
- Add styled enable card to L4 dialog matching proxy host pattern
- Unify section colors across proxy host and L4 dialogs (cyan=LB, emerald=DNS, violet=upstream DNS, rose=geo, amber=mTLS)
- Improve light mode contrast: muted-foreground oklch 0.552→0.502, remove opacity modifiers on secondary text
- Improve dark mode: boost muted-foreground to 0.85, increase border opacity 10%→16%, input 15%→20%
- Add bg-card to DataTable wrapper and bg-muted/40 to table headers for surface hierarchy
- Add semantic badge variants (success, warning, info, muted) and StatusChip dark mode fix
- Add server-side sortable columns to Proxy Hosts and L4 Proxy Hosts (name, upstream, status, protocol, listen)
- Add sortKey to DataTable Column type with clickable sort headers (ArrowUp/Down indicators, URL param driven)
- Fix E2E test selectors for shadcn UI (label associations, combobox roles, dropdown menus, mobile drawer)
- Add htmlFor/id to proxy host form fields and aria-labels to select triggers for accessibility
- Add sorting E2E tests for both proxy host pages

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:17:56 +01:00

76 lines
1.8 KiB
TypeScript

import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogFooter,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { ReactNode } from "react";
type AppDialogProps = {
open: boolean;
onClose: () => void;
title: string;
children: ReactNode;
maxWidth?: "xs" | "sm" | "md" | "lg" | "xl";
actions?: ReactNode;
submitLabel?: string;
onSubmit?: () => void;
isSubmitting?: boolean;
};
const MAX_WIDTH_CLASS: Record<NonNullable<AppDialogProps["maxWidth"]>, string> = {
xs: "max-w-xs",
sm: "max-w-sm",
md: "max-w-md",
lg: "max-w-2xl",
xl: "max-w-4xl",
};
export function AppDialog({
open,
onClose,
title,
children,
maxWidth = "sm",
actions,
submitLabel = "Save",
onSubmit,
isSubmitting = false,
}: AppDialogProps) {
return (
<Dialog open={open} onOpenChange={(o) => !o && onClose()}>
<DialogContent className={MAX_WIDTH_CLASS[maxWidth]}>
<DialogHeader>
<DialogTitle>{title}</DialogTitle>
</DialogHeader>
<div className="flex-1 min-h-0 overflow-y-auto py-4 px-1">{children}</div>
<DialogFooter>
{actions ?? (
<>
<Button variant="outline" onClick={onClose}>
Cancel
</Button>
{onSubmit && (
<Button onClick={onSubmit} disabled={isSubmitting}>
{isSubmitting ? (
<>
<span className="h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent mr-2" />
Saving
</>
) : (
submitLabel
)}
</Button>
)}
</>
)}
</DialogFooter>
</DialogContent>
</Dialog>
);
}