fix(web): keep API key table header and rows aligned on scroll

The secret-key modal rendered the header and the rows in separate
containers with fixed pixel column widths. Adding the Scope column pushed
the total width past the modal, so the body scrolled horizontally on its
own and desynced from the fixed header.

Switch to proportional flex columns (header and rows share the same
weights) with min-w-0 + truncate, and constrain the body to vertical
scroll only. Columns now always fit the modal width, so the header and
values stay aligned. The scope cell gains a title tooltip for the full
label when truncated.
This commit is contained in:
yungle246 2026-06-18 17:59:54 +09:00 committed by YungLe
parent 1bb3e360e3
commit 8391cf7d05

View File

@ -157,20 +157,20 @@ const SecretKeyModal = ({
!!apiKeysList?.data?.length && (
<div className="mt-4 flex grow flex-col overflow-hidden">
<div className="flex h-9 shrink-0 items-center border-b border-divider-regular text-xs font-semibold text-text-tertiary">
<div className="w-64 shrink-0 px-3">{t('apiKeyModal.secretKey', { ns: 'appApi' })}</div>
{!appId && <div className="w-[180px] shrink-0 px-3">{t('apiKeyModal.scope', { ns: 'appApi' })}</div>}
<div className="w-[200px] shrink-0 px-3">{t('apiKeyModal.created', { ns: 'appApi' })}</div>
<div className="w-[200px] shrink-0 px-3">{t('apiKeyModal.lastUsed', { ns: 'appApi' })}</div>
<div className="grow px-3"></div>
<div className="min-w-0 flex-[1.8] truncate px-3">{t('apiKeyModal.secretKey', { ns: 'appApi' })}</div>
{!appId && <div className="min-w-0 flex-[1.3] truncate px-3">{t('apiKeyModal.scope', { ns: 'appApi' })}</div>}
<div className="min-w-0 flex-[1.8] truncate px-3">{t('apiKeyModal.created', { ns: 'appApi' })}</div>
<div className="min-w-0 flex-[1.8] truncate px-3">{t('apiKeyModal.lastUsed', { ns: 'appApi' })}</div>
<div className="w-20 shrink-0 px-3"></div>
</div>
<div className="grow overflow-auto">
<div className="grow overflow-x-hidden overflow-y-auto">
{apiKeysList.data.map(api => (
<div className="flex h-9 items-center border-b border-divider-regular text-sm font-normal text-text-secondary" key={api.id}>
<div className="w-64 shrink-0 truncate px-3 font-mono">{generateToken(api.token)}</div>
{!appId && <div className="w-[180px] shrink-0 truncate px-3">{getScopeLabel(api.dataset_id)}</div>}
<div className="w-[200px] shrink-0 truncate px-3">{formatTime(Number(api.created_at), t('dateTimeFormat', { ns: 'appLog' }) as string)}</div>
<div className="w-[200px] shrink-0 truncate px-3">{api.last_used_at ? formatTime(Number(api.last_used_at), t('dateTimeFormat', { ns: 'appLog' }) as string) : t('never', { ns: 'appApi' })}</div>
<div className="flex grow space-x-2 px-3">
<div className="min-w-0 flex-[1.8] truncate px-3 font-mono">{generateToken(api.token)}</div>
{!appId && <div className="min-w-0 flex-[1.3] truncate px-3" title={getScopeLabel(api.dataset_id)}>{getScopeLabel(api.dataset_id)}</div>}
<div className="min-w-0 flex-[1.8] truncate px-3">{formatTime(Number(api.created_at), t('dateTimeFormat', { ns: 'appLog' }) as string)}</div>
<div className="min-w-0 flex-[1.8] truncate px-3">{api.last_used_at ? formatTime(Number(api.last_used_at), t('dateTimeFormat', { ns: 'appLog' }) as string) : t('never', { ns: 'appApi' })}</div>
<div className="flex w-20 shrink-0 space-x-2 px-3">
<CopyFeedback content={api.token} />
{isCurrentWorkspaceManager && (
<ActionButton