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>
This commit is contained in:
fuomag9
2026-03-22 22:17:56 +01:00
parent 65753f6a8d
commit 9c60d11c2c
45 changed files with 1616 additions and 1052 deletions

View File

@@ -1,6 +1,7 @@
"use client";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { AlertCircle, CheckCircle2, Clock } from "lucide-react";
import type { CertExpiryStatus } from "../page";
function formatRelative(validTo: string): string {
@@ -10,11 +11,11 @@ function formatRelative(validTo: string): string {
const hours = Math.floor(absDiff / 3600000);
if (diff < 0) {
if (days >= 1) return `EXPIRED ${days} day${days !== 1 ? "s" : ""} ago`;
return `EXPIRED ${hours} hour${hours !== 1 ? "s" : ""} ago`;
if (days >= 1) return `Expired ${days}d ago`;
return `Expired ${hours}h ago`;
}
if (days >= 1) return `in ${days} day${days !== 1 ? "s" : ""}`;
return `in ${hours} hour${hours !== 1 ? "s" : ""}`;
if (days >= 1) return `${days}d`;
return `${hours}h`;
}
function formatFull(validTo: string): string {
@@ -33,24 +34,32 @@ export function RelativeTime({
status: CertExpiryStatus | null;
}) {
if (validTo === null || status === null) {
return (
<p className="text-sm text-muted-foreground"></p>
);
return <span className="text-sm text-muted-foreground"></span>;
}
const colorClass =
const config =
status === "expired"
? "text-destructive"
? {
icon: <AlertCircle className="h-3.5 w-3.5" />,
cls: "border-rose-500/30 bg-rose-500/10 text-rose-600 dark:text-rose-400",
}
: status === "expiring_soon"
? "text-yellow-600 dark:text-yellow-400"
: "text-green-600 dark:text-green-400";
? {
icon: <Clock className="h-3.5 w-3.5" />,
cls: "border-amber-500/30 bg-amber-500/10 text-amber-600 dark:text-amber-400",
}
: {
icon: <CheckCircle2 className="h-3.5 w-3.5" />,
cls: "border-emerald-500/30 bg-emerald-500/10 text-emerald-600 dark:text-emerald-400",
};
return (
<Tooltip>
<TooltipTrigger asChild>
<p className={`text-sm font-medium cursor-default ${colorClass}`}>
<span className={`inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs font-semibold cursor-default ${config.cls}`}>
{config.icon}
{formatRelative(validTo)}
</p>
</span>
</TooltipTrigger>
<TooltipContent>{formatFull(validTo)}</TooltipContent>
</Tooltip>