mirror of
https://github.com/vxcontrol/pentagi.git
synced 2026-07-21 08:15:23 -04:00
fix(frontend): UX consistency sweep — dialog case, page titles, clone URL
Resolves QA report observations O7, O8, O10, O11, O12.
O7 — Confirm dialog title case
The Resources page rendered "Delete Directory" / "Delete Resource" in
Title Case while every other delete confirm (template, knowledge,
token) used Sentence case ("Delete template", "Delete token"). Aligned
the resources copy to "Delete directory" / "Delete resource".
O8 — /settings/api-tokens identity mismatch
Three names referred to the same page:
- sidebar link / h1: "PentAGI API"
- tab title (route registry): "API tokens"
- menuItems entry: "PentAGI API"
Unified everything to "API Tokens". Also removed the now-redundant
api-tokens special case in settings-layout's SettingsHeader (it always
returned the same value the menuItems lookup would have produced).
O10 — Provider Clone URL parameter
`/settings/providers/new?id=5` collided semantically with the rest of
the codebase, where `?id=` usually means "this is the record being
edited". Renamed to `?clone=5` so the intent is explicit when the URL
is shared or shows up in logs. Updated the reader in
settings-provider.tsx (`formQueryParams.id` → `formQueryParams.clone`)
to match.
O11 — "Create Provider" button accessibility
The button is a DropdownMenuTrigger that opens a provider-type chooser
before navigating to /new — not a submit button. Sighted users get
this from `<ChevronDown />` and Radix's `aria-haspopup="menu"`, but
the bare visible text "Create Provider" reads as a confirmation
action to screen readers. Added `aria-label="Create provider — choose
type"` and a block comment explaining the pattern so future
maintainers don't try to "fix" the dropdown into a direct submit.
O12 — `(unnamed)` placeholder rendering
Tokens with empty name rendered as literal `(unnamed)` with
text-muted-foreground only. Added `font-normal italic` so the
placeholder reads as missing data rather than a real name (the column
otherwise uses font-medium).
Verified: 0 ESLint errors, 508/508 tests pass.
Browser regression sweep across 9 pages (/dashboard, /flows,
/templates, /knowledges, /resources, /flows/:id, /settings/{providers,
api-tokens,prompts}) still returns 0 icon-only buttons without an
accessible name.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -47,7 +47,7 @@ const menuItems: readonly MenuItem[] = [
|
||||
icon: <Key className="size-4" />,
|
||||
id: 'api-tokens',
|
||||
path: '/settings/api-tokens',
|
||||
title: 'PentAGI API',
|
||||
title: 'API Tokens',
|
||||
},
|
||||
] as const;
|
||||
|
||||
@@ -74,10 +74,6 @@ function SettingsHeader() {
|
||||
return 'Edit Prompt';
|
||||
}
|
||||
|
||||
if (path === '/settings/api-tokens') {
|
||||
return 'PentAGI API';
|
||||
}
|
||||
|
||||
const activeItem = menuItems.find((item) => path.startsWith(item.path));
|
||||
|
||||
return activeItem?.title ?? 'Settings';
|
||||
|
||||
@@ -34,7 +34,7 @@ export type TitleResolver = ((params: RouteParams) => string) | ComponentType<{
|
||||
* from this registry onto the matching <Route>.
|
||||
*/
|
||||
export const routeTitles = {
|
||||
apiTokens: { title: 'API tokens' },
|
||||
apiTokens: { title: 'API Tokens' },
|
||||
dashboard: { title: 'Dashboard' },
|
||||
flow: {
|
||||
title: apolloTitle({
|
||||
|
||||
@@ -600,7 +600,7 @@ function Resources() {
|
||||
isOpen={!!deletion.fileToDelete}
|
||||
itemName={deletion.fileToDelete?.name}
|
||||
itemType={deletion.fileToDelete?.isDir ? 'directory' : 'resource'}
|
||||
title={deletion.fileToDelete?.isDir ? 'Delete Directory' : 'Delete Resource'}
|
||||
title={deletion.fileToDelete?.isDir ? 'Delete directory' : 'Delete resource'}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -504,7 +504,9 @@ function SettingsAPITokens() {
|
||||
|
||||
return (
|
||||
<div className="font-medium">
|
||||
{token.name || <span className="text-muted-foreground">(unnamed)</span>}
|
||||
{token.name || (
|
||||
<span className="text-muted-foreground font-normal italic">(unnamed)</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -877,7 +877,7 @@ function SettingsProvider() {
|
||||
|
||||
const formQueryParams = useMemo(
|
||||
() => ({
|
||||
id: searchParams.get('id'),
|
||||
clone: searchParams.get('clone'),
|
||||
type: searchParams.get('type'),
|
||||
}),
|
||||
[searchParams],
|
||||
@@ -1001,7 +1001,7 @@ function SettingsProvider() {
|
||||
|
||||
if (isNew || !providerId) {
|
||||
const queryType = formQueryParams.type ?? undefined;
|
||||
const queryId = formQueryParams.id;
|
||||
const queryId = formQueryParams.clone;
|
||||
|
||||
if (queryId && data?.settingsProviders?.userDefined) {
|
||||
const sourceProvider = data.settingsProviders.userDefined.find((p: Provider) => p.id == queryId);
|
||||
|
||||
@@ -103,7 +103,7 @@ function SettingsProviders() {
|
||||
|
||||
const handleProviderClone = useCallback(
|
||||
(providerId: string) => {
|
||||
navigate(`/settings/providers/new?id=${providerId}`);
|
||||
navigate(`/settings/providers/new?clone=${providerId}`);
|
||||
},
|
||||
[navigate],
|
||||
);
|
||||
@@ -451,9 +451,18 @@ function SettingsProvidersHeader() {
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<p className="text-muted-foreground min-w-0 flex-1 truncate">Manage language model providers</p>
|
||||
|
||||
{/*
|
||||
* "Create Provider" is a dropdown trigger, not a submit-style action — it
|
||||
* opens a menu listing provider types (OpenAI, Anthropic, Custom, …). The
|
||||
* `<ChevronDown />` icon plus Radix's `aria-haspopup="menu"` already signal
|
||||
* "menu opens" to sighted and AT users; the explicit aria-label adds the
|
||||
* intent ("create provider") so screen readers don't just announce
|
||||
* "Create Provider, menu" but "Create provider, choose type, menu".
|
||||
*/}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
aria-label="Create provider — choose type"
|
||||
className="shrink-0"
|
||||
variant="secondary"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user