From 9058d4428575d4855fbde46df31c4a4e3b452c14 Mon Sep 17 00:00:00 2001 From: overtrue Date: Mon, 15 Jun 2026 12:42:44 +0800 Subject: [PATCH] fix: polish ui details and add audit skill --- app/(auth)/auth/oidc-callback/page.tsx | 6 +- app/(auth)/config/page.tsx | 45 ++-- app/(auth)/layout.tsx | 6 +- app/(dashboard)/403/page.tsx | 10 +- .../performance-infrastructure-card.tsx | 12 +- .../_components/performance-server-list.tsx | 6 +- .../_components/performance-usage-card.tsx | 4 +- app/(dashboard)/access-keys/page.tsx | 12 +- app/(dashboard)/browser/content.tsx | 21 +- app/(dashboard)/browser/page.tsx | 23 +- app/(dashboard)/buckets/page.tsx | 2 +- app/(dashboard)/events/page.tsx | 2 +- app/(dashboard)/import-export/page.tsx | 22 +- app/(dashboard)/layout.tsx | 6 +- app/(dashboard)/lifecycle/page.tsx | 2 +- app/(dashboard)/policies/page.tsx | 6 +- app/(dashboard)/pool-decommission/page.tsx | 14 +- app/(dashboard)/rebalance/page.tsx | 10 +- app/(dashboard)/replication/page.tsx | 2 +- app/(dashboard)/site-replication/page.tsx | 13 +- app/(dashboard)/sse/page.tsx | 164 ++++++++---- app/(dashboard)/status/page.tsx | 2 +- app/(dashboard)/user-groups/page.tsx | 8 +- app/(dashboard)/users/page.tsx | 26 +- app/globals.css | 33 +++ components/access-keys/edit-item.tsx | 50 ++-- components/access-keys/new-item.tsx | 47 +++- components/app-sidebar.tsx | 18 +- components/app-top-nav.tsx | 10 +- components/audit-target/new-form.tsx | 27 +- components/auth/heroes/hero-static.tsx | 17 +- components/auth/login-form.tsx | 37 ++- components/buckets/events-tab.tsx | 4 +- components/buckets/info.tsx | 123 ++++++--- components/buckets/lifecycle-tab.tsx | 4 +- components/buckets/list.tsx | 18 +- components/buckets/new-form.tsx | 81 ++++-- components/buckets/replication-tab.tsx | 4 +- components/buckets/selector.tsx | 2 +- components/copy-button.tsx | 2 +- components/copy-input.tsx | 3 +- .../data-table/data-table-pagination.tsx | 2 +- components/data-table/data-table.tsx | 16 +- components/datetime-picker.tsx | 6 +- components/events-target/new-form.tsx | 27 +- components/events/new-form.tsx | 21 +- components/language-switcher.tsx | 6 +- components/license/article.tsx | 6 +- components/license/enterprise-section.tsx | 5 +- components/lifecycle/new-form.tsx | 63 ++++- components/links/github.tsx | 11 +- components/links/x.tsx | 11 +- components/object/info.tsx | 92 +++++-- components/object/list.tsx | 57 +++-- components/object/new-form.tsx | 3 + components/object/parquet-viewer.tsx | 2 +- components/object/path-links.tsx | 30 ++- components/object/pdf-viewer.tsx | 6 +- components/object/preview-modal.tsx | 20 +- components/object/upload-picker.tsx | 52 ++-- components/object/versions.tsx | 27 +- components/object/view.tsx | 2 +- components/oidc/form.tsx | 58 ++++- components/oidc/provider-list.tsx | 9 +- components/page-header.tsx | 15 +- components/policies/form.tsx | 13 +- components/pools/overview.tsx | 16 +- components/providers/app-ui-provider.tsx | 2 + components/providers/theme-provider.tsx | 36 ++- components/replication/new-form.tsx | 132 +++++++--- components/search-input.tsx | 33 ++- components/site-replication/edit-form.tsx | 18 +- components/site-replication/new-form.tsx | 35 ++- components/skip-link.tsx | 16 ++ components/tasks/item.tsx | 3 +- components/tasks/panel.tsx | 6 +- components/tasks/stats-button.tsx | 2 +- components/theme-switcher.tsx | 6 +- components/theme/logo.tsx | 8 +- components/tiers/change-key.tsx | 10 +- components/tiers/new-form.tsx | 72 ++++-- components/top-nav-breadcrumb.tsx | 30 +-- components/upload-zone.tsx | 8 +- components/user-group/edit-form.tsx | 10 +- components/user-group/members.tsx | 2 +- components/user-group/new-form.tsx | 15 +- components/user-group/policies.tsx | 4 +- .../user-group/set-policies-multiple.tsx | 2 + components/user/add-to-group-form.tsx | 10 +- components/user/change-password.tsx | 23 +- components/user/dropdown.tsx | 12 +- components/user/edit-form.tsx | 11 +- components/user/edit/access-keys.tsx | 117 ++++++--- components/user/edit/groups.tsx | 2 +- components/user/edit/policies.tsx | 2 +- components/user/edit/secret-key.tsx | 7 +- components/user/new-form.tsx | 32 ++- components/user/selector.tsx | 2 +- eslint.config.js | 7 +- i18n/locales/ar-MA.json | 40 +-- i18n/locales/de-DE.json | 40 +-- i18n/locales/en-US.json | 40 +-- i18n/locales/es-ES.json | 40 +-- i18n/locales/fr-FR.json | 40 +-- i18n/locales/id-ID.json | 40 +-- i18n/locales/it-IT.json | 40 +-- i18n/locales/ja-JP.json | 40 +-- i18n/locales/ko-KR.json | 40 +-- i18n/locales/pt-BR.json | 40 +-- i18n/locales/ru-RU.json | 40 +-- i18n/locales/tr-TR.json | 40 +-- i18n/locales/vi-VN.json | 40 +-- i18n/locales/zh-CN.json | 40 +-- lib/functions.ts | 26 ++ pnpm-lock.yaml | 235 ------------------ skills/ui-audit/SKILL.md | 140 +++++++++++ skills/ui-audit/agents/openai.yaml | 4 + 117 files changed, 1997 insertions(+), 1123 deletions(-) create mode 100644 components/skip-link.tsx create mode 100644 skills/ui-audit/SKILL.md create mode 100644 skills/ui-audit/agents/openai.yaml diff --git a/app/(auth)/auth/oidc-callback/page.tsx b/app/(auth)/auth/oidc-callback/page.tsx index 1918f150..8d333cd6 100644 --- a/app/(auth)/auth/oidc-callback/page.tsx +++ b/app/(auth)/auth/oidc-callback/page.tsx @@ -69,10 +69,10 @@ export default function OidcCallbackPage() { }, [credentialsSet, isAuthenticated, hasResolvedFirstRoute, firstAccessibleRoute, router]) return ( -
+
-
-

{t("Completing SSO login...")}

+
+

{t("Completing SSO login…")}

) diff --git a/app/(auth)/config/page.tsx b/app/(auth)/config/page.tsx index 5501e87d..352cd98b 100644 --- a/app/(auth)/config/page.tsx +++ b/app/(auth)/config/page.tsx @@ -101,25 +101,25 @@ function ConfigPageContent() { } return ( -
+
-
+
-
+
- +
-

{t("Server Configuration")}

-

- {t("Please configure your RustFS server address")} -

+

{t("Server Configuration")}

+

{t("Please configure your RustFS server address")}

@@ -131,9 +131,12 @@ function ConfigPageContent() { setServerHost(e.target.value)} - type="text" + type="url" + autoComplete="off" + spellCheck={false} placeholder={t("Please enter server address (e.g., http://localhost:9000)")} /> @@ -142,16 +145,28 @@ function ConfigPageContent() { -
- - -
@@ -160,9 +175,9 @@ function ConfigPageContent() {
-

+

{t("Need help?")}{" "} - + {t("View Documentation")}

diff --git a/app/(auth)/layout.tsx b/app/(auth)/layout.tsx index bb664d41..c93eac6b 100644 --- a/app/(auth)/layout.tsx +++ b/app/(auth)/layout.tsx @@ -1,3 +1,7 @@ export default function AuthLayout({ children }: { children: React.ReactNode }) { - return
{children}
+ return ( +
+ {children} +
+ ) } diff --git a/app/(dashboard)/403/page.tsx b/app/(dashboard)/403/page.tsx index 18b05830..76983d97 100644 --- a/app/(dashboard)/403/page.tsx +++ b/app/(dashboard)/403/page.tsx @@ -31,17 +31,17 @@ export default function ForbiddenPage() { - - + + - - + + {t("Access Denied")} diff --git a/app/(dashboard)/_components/performance-infrastructure-card.tsx b/app/(dashboard)/_components/performance-infrastructure-card.tsx index dc9d81e7..61434bcc 100644 --- a/app/(dashboard)/_components/performance-infrastructure-card.tsx +++ b/app/(dashboard)/_components/performance-infrastructure-card.tsx @@ -23,27 +23,27 @@ export function PerformanceInfrastructureCard({
-
+

{t("Servers")}

-
+

{t("Online")}

{onlineServers}

-
+

{t("Offline")}

{offlineServers}

-
+

{t("Disks")}

-
+

{t("Online")}

{onlineDisks}

-
+

{t("Offline")}

{offlineDisks}

diff --git a/app/(dashboard)/_components/performance-server-list.tsx b/app/(dashboard)/_components/performance-server-list.tsx index 9b84dad3..af9ef8d4 100644 --- a/app/(dashboard)/_components/performance-server-list.tsx +++ b/app/(dashboard)/_components/performance-server-list.tsx @@ -147,7 +147,7 @@ export function PerformanceServerList({ servers, t }: { servers: ServerInfo[]; t
{t("Sort by")} updateFormState("defaultKeyId", event.target.value)} + autoComplete="off" placeholder={t("Enter the default SSE key ID")} + spellCheck={false} /> @@ -780,23 +782,31 @@ export default function SSEPage() { {formState.backendType === "local" ? ( - {t("Local Key Directory")} + {t("Local Key Directory")} updateFormState("keyDir", event.target.value)} + autoComplete="off" placeholder={t("Enter an absolute path such as D:/data/kms-keys")} + spellCheck={false} /> {t("The directory must be an absolute path on the server.")} - {t("File Permissions")} + {t("File Permissions")} updateFormState("filePermissions", event.target.value)} placeholder="384" @@ -816,20 +826,27 @@ export default function SSEPage() { - {t("Vault Server Address")} + {t("Vault Server Address")} updateFormState("address", event.target.value)} + autoComplete="off" placeholder="http://127.0.0.1:8200" + spellCheck={false} /> - {t("Vault Token")} + {t("Vault Token")} updateFormState("vaultToken", event.target.value)} @@ -839,6 +856,7 @@ export default function SSEPage() { : t("Enter your Vault authentication token") } autoComplete="off" + spellCheck={false} /> @@ -851,23 +869,31 @@ export default function SSEPage() { - {t("Vault Namespace")} + {t("Vault Namespace")} updateFormState("namespace", event.target.value)} + autoComplete="off" placeholder={t("Optional Vault namespace")} + spellCheck={false} /> - {t("Transit Mount Path")} + {t("Transit Mount Path")} updateFormState("mountPath", event.target.value)} + autoComplete="off" placeholder="transit" + spellCheck={false} /> @@ -875,23 +901,31 @@ export default function SSEPage() { {formState.backendType === "vault-kv2" && ( <> - {t("KV Mount")} + {t("KV Mount")} updateFormState("kvMount", event.target.value)} + autoComplete="off" placeholder="secret" + spellCheck={false} /> - {t("Key Path Prefix")} + {t("Key Path Prefix")} updateFormState("keyPathPrefix", event.target.value)} + autoComplete="off" placeholder="rustfs/kms/keys" + spellCheck={false} /> @@ -899,8 +933,9 @@ export default function SSEPage() { )} -
+
updateFormState("skipTlsVerify", checked === true)} id="skipTlsVerify" @@ -914,11 +949,15 @@ export default function SSEPage() { - {t("Timeout (seconds)")} + {t("Timeout (seconds)")} updateFormState("timeoutSeconds", event.target.value)} /> @@ -926,11 +965,15 @@ export default function SSEPage() { - {t("Retry Attempts")} + {t("Retry Attempts")} updateFormState("retryAttempts", event.target.value)} /> @@ -938,9 +981,10 @@ export default function SSEPage() { -
+
updateFormState("enableCache", checked === true)} id="enableCache" @@ -953,11 +997,15 @@ export default function SSEPage() { {formState.enableCache && ( - {t("Max Cached Keys")} + {t("Max Cached Keys")} updateFormState("maxCachedKeys", event.target.value)} /> @@ -965,11 +1013,15 @@ export default function SSEPage() { - {t("Cache TTL (seconds)")} + {t("Cache TTL (seconds)")} updateFormState("cacheTtlSeconds", event.target.value)} /> @@ -1014,11 +1066,11 @@ export default function SSEPage() { disabled={loadingKeys} onClick={() => void loadKeys(currentMarker)} > - {loadingKeys ? : } + {loadingKeys ? : } {t("Refresh")}
@@ -1033,11 +1085,11 @@ export default function SSEPage() { {loadingKeys ? ( -
+
) : keys.length === 0 ? ( -
+
{t("No KMS keys found")}
) : ( @@ -1050,7 +1102,7 @@ export default function SSEPage() { {t("Algorithm")} {t("Usage")} {t("Created")} - {t("Actions")} + {t("Actions")} @@ -1129,7 +1181,7 @@ export default function SSEPage() { void loadKeys(previousMarker) }} > - + {t("Previous")}
@@ -1164,7 +1216,7 @@ export default function SSEPage() { } }} > - + {t("Create New Key")} @@ -1174,29 +1226,37 @@ export default function SSEPage() {
- {t("Key Name")} + {t("Key Name")} setCreateKeyName(event.target.value)} + autoComplete="off" placeholder={t("Optional display name for the key")} + spellCheck={false} /> - {t("Description")} + {t("Description")} setCreateKeyDescription(event.target.value)} + autoComplete="off" placeholder={t("Describe the purpose of this key")} /> -
+
setCreateKeySetAsDefault(checked === true)} id="setAsDefaultKey" @@ -1235,56 +1295,56 @@ export default function SSEPage() {
) : !keyDetails ? ( -
+
{t("No key details available")}
) : ( <>
-
+

{t("Name")}

{getKeyDisplayName(keyDetails)}

-
+

{t("State")}

{keyDetails.key_state || "-"}
-
+

{t("KMS Key ID")}

{keyDetails.key_id}

-
+

{t("Usage")}

{keyDetails.key_usage || "-"}

-
+

{t("Creation Date")}

{formatTimestamp(keyDetails.creation_date)}

-
+

{t("Deletion Date")}

{formatTimestamp(keyDetails.deletion_date)}

-
+

{t("Origin")}

{keyDetails.origin || "-"}

-
+

{t("Key Manager")}

{keyDetails.key_manager || "-"}

{keyDetails.description && ( -
+

{t("Description")}

{keyDetails.description}

)} -
+

{t("Tags")}

{Object.entries(keyDetails.tags ?? {}).length > 0 ? ( diff --git a/app/(dashboard)/status/page.tsx b/app/(dashboard)/status/page.tsx index 58e47590..8e11c02c 100644 --- a/app/(dashboard)/status/page.tsx +++ b/app/(dashboard)/status/page.tsx @@ -172,7 +172,7 @@ export default function PerformancePage() { >

{t("Running Status")}

-
+

{error}

diff --git a/app/(dashboard)/user-groups/page.tsx b/app/(dashboard)/user-groups/page.tsx index 7660dcbc..0556bf63 100644 --- a/app/(dashboard)/user-groups/page.tsx +++ b/app/(dashboard)/user-groups/page.tsx @@ -118,11 +118,11 @@ export default function UserGroupsPage() { cell: ({ row }) => (
@@ -163,11 +163,11 @@ export default function UserGroupsPage() { disabled={!selectedKeys.length} onClick={() => setPoliciesDialogOpen(true)} > - + {t("Assign Policy")} diff --git a/app/(dashboard)/users/page.tsx b/app/(dashboard)/users/page.tsx index ec607a1e..0f72846e 100644 --- a/app/(dashboard)/users/page.tsx +++ b/app/(dashboard)/users/page.tsx @@ -3,7 +3,6 @@ import * as React from "react" import { useState, useEffect } from "react" import { useTranslation } from "react-i18next" -import dayjs from "dayjs" import { RiAddLine, RiDeleteBin5Line, RiEdit2Line, RiGroup2Fill } from "@remixicon/react" import { Button } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" @@ -20,6 +19,7 @@ import { UserAddToGroupForm } from "@/components/user/add-to-group-form" import { useUsers } from "@/hooks/use-users" import { useDialog } from "@/lib/feedback/dialog" import { useMessage } from "@/lib/feedback/message" +import { formatDateTime } from "@/lib/functions" import type { ColumnDef } from "@tanstack/react-table" interface UserRow { @@ -33,15 +33,15 @@ interface UserRow { function formatDate(dateStr?: string) { if (!dateStr) return "-" - let d = dayjs(dateStr) - if (d.isValid()) return d.format("YYYY-MM-DD HH:mm:ss") - const [date, timeRaw, offsetRaw] = (dateStr || "").split(" ") - if (date && timeRaw && offsetRaw) { + const parsedDate = new Date(dateStr) + if (!Number.isNaN(parsedDate.getTime())) return formatDateTime(parsedDate) + const [datePart, timeRaw, offsetRaw] = (dateStr || "").split(" ") + if (datePart && timeRaw && offsetRaw) { const time = timeRaw.substring(0, 12) const offset = offsetRaw.substring(0, 6) - const isoStr = `${date}T${time}${offset}` - d = dayjs(isoStr) - if (d.isValid()) return d.format("YYYY-MM-DD HH:mm:ss") + const isoStr = `${datePart}T${time}${offset}` + const normalizedDate = new Date(isoStr) + if (!Number.isNaN(normalizedDate.getTime())) return formatDateTime(normalizedDate) } return "-" } @@ -134,13 +134,13 @@ export default function UsersPage() {
{canEditUser ? ( ) : null} {canDeleteUser ? ( ) : null} @@ -235,7 +235,7 @@ export default function UsersPage() { disabled={!canBulkDeleteUsers || !selectedKeys.length} onClick={deleteByList} > - + {t("Delete Selected")} {canCreateUser ? ( ) : null} diff --git a/app/globals.css b/app/globals.css index d0b2354e..45f39cef 100644 --- a/app/globals.css +++ b/app/globals.css @@ -48,6 +48,7 @@ } :root { + color-scheme: light; --card: oklch(1 0 0); --card-foreground: oklch(0.145 0 0); --popover: oklch(1 0 0); @@ -83,6 +84,7 @@ } .dark { + color-scheme: dark; --background: oklch(0.145 0 0); --foreground: oklch(0.985 0 0); --card: oklch(0.205 0 0); @@ -125,6 +127,37 @@ } html { @apply font-sans; + -webkit-tap-highlight-color: transparent; + } + button, + a, + input, + textarea, + select, + [role="button"] { + touch-action: manipulation !important; + } + [data-slot="tabs-list"] { + min-height: 2.25rem; + } + [data-slot="tabs-trigger"] { + min-height: 2rem; + } + [data-slot="dialog-content"], + [data-slot="drawer-content"], + [data-slot="dropdown-menu-content"], + [data-slot="popover-content"] { + overscroll-behavior: contain; + } + @media (prefers-reduced-motion: reduce) { + *, + ::before, + ::after { + animation-duration: 1ms !important; + animation-iteration-count: 1 !important; + scroll-behavior: auto !important; + transition-duration: 1ms !important; + } } } diff --git a/components/access-keys/edit-item.tsx b/components/access-keys/edit-item.tsx index 31fd8305..42cda218 100644 --- a/components/access-keys/edit-item.tsx +++ b/components/access-keys/edit-item.tsx @@ -142,57 +142,79 @@ export function AccessKeysEditItem({ open, onOpenChange, row, onSuccess }: Acces return ( - + {t("Edit Key")} -
+
- {t("Access Key")} + {t("Access Key")} - + - {t("Expiry")} + {t("Expiry")} - + - {t("Name")} + {t("Name")} - setName(e.target.value)} /> + setName(e.target.value)} + autoComplete="off" + spellCheck={false} + /> - {t("Description")} + {t("Description")} -