From 0cfae1799d07368ebbdcb31f28c269b55f9343d7 Mon Sep 17 00:00:00 2001 From: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> Date: Wed, 14 Oct 2026 14:18:59 +0800 Subject: [PATCH] fix(web): reduce invitation dialog control density Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> --- .../add-namespace-invite-links/design.md | 2 ++ .../namespace/invitation-controls.tsx | 16 ++++----- .../features/namespace/invitation-history.tsx | 36 +++++++++---------- web/src/shared/components/pagination.tsx | 16 +++++---- 4 files changed, 37 insertions(+), 33 deletions(-) diff --git a/openspec/changes/add-namespace-invite-links/design.md b/openspec/changes/add-namespace-invite-links/design.md index bc814851..1dc5337c 100644 --- a/openspec/changes/add-namespace-invite-links/design.md +++ b/openspec/changes/add-namespace-invite-links/design.md @@ -48,3 +48,5 @@ 新增历史查询沿用Session认证、禁止缓存及空间OWNER/ADMIN权限;每页数量有上限,按创建时间倒序和独立标识稳定排序。创建/预览/接受现有响应合同保持兼容。审计新增独立邀请标识关联,不包含任何凭证。 邀请历史列表网页每页5条并保持紧凑高度,数据保留规则不变。人员主要展示姓名快照,不以当前账号姓名替换历史;完整稳定账号ID作为悬停/可访问说明,未记录姓名时用短标识兜底,不增加邮箱采集或展示。 + +弹窗内采用紧凑操作尺寸:主要操作按钮32px、页码28px,收紧按钮与分页间距,日期和数量文字适当缩小;共用分页保留默认样式,仅邀请历史启用紧凑模式。640px弹窗宽度、每页5条及240px列表上限不变。 diff --git a/web/src/features/namespace/invitation-controls.tsx b/web/src/features/namespace/invitation-controls.tsx index d2323d3f..81723a1a 100644 --- a/web/src/features/namespace/invitation-controls.tsx +++ b/web/src/features/namespace/invitation-controls.tsx @@ -105,7 +105,7 @@ export function InvitationControls({ slug, compact = false }: { slug: string; co
- + {status.isFetching ? @@ -114,8 +114,8 @@ export function InvitationControls({ slug, compact = false }: { slug: string; co
- event.currentTarget.select()} /> - + event.currentTarget.select()} /> +
{expiresAt ?

{t('invitation.expiresAtLabel')}: {expiresAt}

: null}

{t(storageUnavailable ? 'invitation.storageUnavailable' : 'invitation.localLinkHint')}

@@ -123,10 +123,10 @@ export function InvitationControls({ slug, compact = false }: { slug: string; co ) : null} {!link && !status.isFetching && !status.isError && status.data?.active ?

{t('invitation.activeUnavailable')}

: null} {!status.isError && !status.data?.active ? ( -
+
) : null}
- ) : } + ) : } {!compact ? (

{t('invitation.detailsTitle')}

@@ -137,15 +137,15 @@ export function InvitationControls({ slug, compact = false }: { slug: string; co {(create.error || revoke.error || status.error) ? (

{t('invitation.error')}

- {status.error ? : null} + {status.error ? : null}
) : null} {!compact && !status.isError && status.data?.active ? (
{t('invitation.managementTitle')}
- - + +
) : null} diff --git a/web/src/features/namespace/invitation-history.tsx b/web/src/features/namespace/invitation-history.tsx index 3c5e06f0..460f8a5e 100644 --- a/web/src/features/namespace/invitation-history.tsx +++ b/web/src/features/namespace/invitation-history.tsx @@ -39,54 +39,54 @@ export function InvitationHistory({ slug }: { slug: string }) { } const status = (item: NamespaceInvitationHistory) => ( - - {t(statusKeys[item.status])} - {item.legacy ? {t('invitation.historyLegacy')} : null} + + {t(statusKeys[item.status])} + {item.legacy ? {t('invitation.historyLegacy')} : null} ) const joined = (item: NamespaceInvitationHistory) => item.consumedAt ? ( - <>{identity(item.consumedByName, item.consumedBy)} + <>{identity(item.consumedByName, item.consumedBy)} ) : {t('invitation.historyNoJoin')} return (

{t('invitation.historyTitle')}

-

{t('invitation.historyDescription')}

{history.isPending ?

: history.isError ? ( -

{t('invitation.historyError')}

+

{t('invitation.historyError')}

) : history.data?.items.length ? ( <>
- - - - + + + + {history.data.items.map((item) => - - - - + + + + )}
{t('invitation.historyTitle')}
{t('invitation.historyCreator')}{t('invitation.historyCreatedAt')} / {t('invitation.historyExpiresAt')}{t('invitation.historyStatus')}{t('invitation.historyJoined')}{t('invitation.historyCreator')}{t('invitation.historyCreatedAt')} / {t('invitation.historyExpiresAt')}{t('invitation.historyStatus')}{t('invitation.historyJoined')}
{identity(item.issuedByName, item.issuedBy)}{status(item)}{joined(item)}{identity(item.issuedByName, item.issuedBy)}{status(item)}{joined(item)}
-
    {history.data.items.map((item) =>
  • +
      {history.data.items.map((item) =>
    • {t('invitation.historyCreator')}: {identity(item.issuedByName, item.issuedBy)}
      {status(item)}
      -
      +
      {t('invitation.historyCreatedAt')}
      {t('invitation.historyExpiresAt')}
      {t('invitation.historyJoined')}
      {joined(item)}
    • )}
- {history.data.totalPages > 1 ? : null} + {history.data.totalPages > 1 ? : null} ) :

{t('invitation.historyEmpty')}

}
diff --git a/web/src/shared/components/pagination.tsx b/web/src/shared/components/pagination.tsx index 01b29042..0c7ae173 100644 --- a/web/src/shared/components/pagination.tsx +++ b/web/src/shared/components/pagination.tsx @@ -1,10 +1,12 @@ import { useTranslation } from 'react-i18next' +import { cn } from '@/shared/lib/utils' import { Button } from '@/shared/ui/button' interface PaginationProps { page: number totalPages: number onPageChange: (page: number) => void + compact?: boolean } type PageItem = number | 'ellipsis' @@ -40,28 +42,28 @@ export function buildPageItems(current: number, totalPages: number): PageItem[] return items } -export function Pagination({ page, totalPages, onPageChange }: PaginationProps) { +export function Pagination({ page, totalPages, onPageChange, compact = false }: PaginationProps) { const { t } = useTranslation() const pageItems = buildPageItems(page, totalPages) return ( -