fix(dashboard): harden projectId handling with edge case fixes

- Validate empty string projectId with typeof check and trim()
- Include projectId in registerAccount return value
- Update projectId when changed (not just when missing)
- Add aria-hidden/aria-label for accessibility
- Add max-width + truncate for long projectId overflow
This commit is contained in:
kaitranntt
2026-01-15 11:46:02 -05:00
parent 36367d49f0
commit bc02ecc94c
2 changed files with 24 additions and 8 deletions
+15 -5
View File
@@ -384,6 +384,7 @@ export function registerAccount(
tokenFile, tokenFile,
createdAt: providerAccounts.accounts[accountId].createdAt, createdAt: providerAccounts.accounts[accountId].createdAt,
lastUsedAt: providerAccounts.accounts[accountId].lastUsedAt, lastUsedAt: providerAccounts.accounts[accountId].lastUsedAt,
projectId: providerAccounts.accounts[accountId].projectId,
}; };
} }
@@ -640,12 +641,17 @@ export function discoverExistingAccounts(): void {
const existingTokenFiles = Object.values(providerAccounts.accounts).map((a) => a.tokenFile); const existingTokenFiles = Object.values(providerAccounts.accounts).map((a) => a.tokenFile);
if (existingTokenFiles.includes(file)) { if (existingTokenFiles.includes(file)) {
// Token file exists - check if we need to update projectId for agy accounts // Token file exists - check if we need to update projectId for agy accounts
if (provider === 'agy' && data.project_id) { const projectIdValue =
typeof data.project_id === 'string' && data.project_id.trim()
? data.project_id.trim()
: null;
if (provider === 'agy' && projectIdValue) {
const existingEntry = Object.entries(providerAccounts.accounts).find( const existingEntry = Object.entries(providerAccounts.accounts).find(
([, meta]) => meta.tokenFile === file ([, meta]) => meta.tokenFile === file
); );
if (existingEntry && !existingEntry[1].projectId) { // Update if missing or changed
existingEntry[1].projectId = data.project_id; if (existingEntry && existingEntry[1].projectId !== projectIdValue) {
existingEntry[1].projectId = projectIdValue;
} }
} }
continue; continue;
@@ -699,8 +705,12 @@ export function discoverExistingAccounts(): void {
}; };
// Read project_id for Antigravity accounts (read-only field from auth token) // Read project_id for Antigravity accounts (read-only field from auth token)
if (provider === 'agy' && data.project_id) { const discoveredProjectId =
accountMeta.projectId = data.project_id; typeof data.project_id === 'string' && data.project_id.trim()
? data.project_id.trim()
: null;
if (provider === 'agy' && discoveredProjectId) {
accountMeta.projectId = discoveredProjectId;
} }
providerAccounts.accounts[accountId] = accountMeta; providerAccounts.accounts[accountId] = accountMeta;
@@ -177,8 +177,14 @@ export function AccountItem({
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<div className="flex items-center gap-1 text-xs text-muted-foreground"> <div className="flex items-center gap-1 text-xs text-muted-foreground">
<FolderCode className="w-3 h-3" /> <FolderCode className="w-3 h-3" aria-hidden="true" />
<span className={cn('font-mono', privacyMode && PRIVACY_BLUR_CLASS)}> <span
className={cn(
'font-mono max-w-[180px] truncate',
privacyMode && PRIVACY_BLUR_CLASS
)}
title={account.projectId}
>
{account.projectId} {account.projectId}
</span> </span>
</div> </div>
@@ -193,7 +199,7 @@ export function AccountItem({
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<div className="flex items-center gap-1 text-xs text-amber-600 dark:text-amber-500"> <div className="flex items-center gap-1 text-xs text-amber-600 dark:text-amber-500">
<AlertTriangle className="w-3 h-3" /> <AlertTriangle className="w-3 h-3" aria-label="Warning" />
<span>Project ID: N/A</span> <span>Project ID: N/A</span>
</div> </div>
</TooltipTrigger> </TooltipTrigger>