Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@
"git-url-parse": "^16.1.0",
"hash-wasm": "^4.12.0",
"history": "^5.3.0",
"i18next": "^26.3.3",
"i18next-browser-languagedetector": "^8.2.1",
"json-diff-kit": "^1.0.35",
"react": "^19.2.7",
"react-dom": "^19.2.7",
"react-i18next": "^17.0.8",
"react-router-dom": "^7.18.0",
"ua-parser-js": "^2.0.10",
"vanilla-jsoneditor": "^3.12.0",
Expand Down
1 change: 1 addition & 0 deletions src/assets/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/logo-h.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 12 additions & 6 deletions src/components/app-detail-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
} from '@ant-design/icons';
import { Breadcrumb, Button, Tag } from 'antd';
import type { ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
import { cn } from '@/utils/helper';
import PlatformIcon from './platform-icon';

Expand All @@ -17,7 +18,7 @@ export interface AppDetailHeaderApp {
export function AppDetailHeader({
activeView,
app,
appNameFallback = '选择应用',
appNameFallback,
managementDisabled,
metricsDisabled,
onManagementClick,
Expand All @@ -37,6 +38,9 @@ export function AppDetailHeader({
sectionLabel: string;
settingsDisabled?: boolean;
}) {
const { t } = useTranslation();
const fallbackName = appNameFallback ?? t('app_detail_header.select_app');

return (
<div className="mb-4 grid grid-cols-1 items-center gap-3 md:grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)]">
<div className="flex min-w-0 items-center justify-between gap-3 md:contents">
Expand All @@ -51,9 +55,11 @@ export function AppDetailHeader({
<span className="inline-flex max-w-full items-center gap-1">
<PlatformIcon platform={app?.platform} className="mr-1" />
<span className="max-w-[160px] truncate md:max-w-none">
{app?.name || appNameFallback}
{app?.name || fallbackName}
</span>
{app?.status === 'paused' && <Tag className="ml-2">暂停</Tag>}
{app?.status === 'paused' && (
<Tag className="ml-2">{t('app_detail_header.paused')}</Tag>
)}
</span>
),
},
Expand All @@ -68,7 +74,7 @@ export function AppDetailHeader({
disabled={settingsDisabled}
onClick={onSettingsClick}
>
应用设置
{t('app_detail_header.app_settings')}
</Button>
)}
</div>
Expand All @@ -82,14 +88,14 @@ export function AppDetailHeader({
active={activeView === 'management'}
disabled={managementDisabled}
icon={<AppstoreOutlined />}
label="应用发布"
label={t('app_detail_header.tab_releases')}
onClick={onManagementClick}
/>
<AppDetailTab
active={activeView === 'metrics'}
disabled={metricsDisabled}
icon={<LineChartOutlined />}
label="实时数据"
label={t('app_detail_header.tab_metrics')}
onClick={onMetricsClick}
/>
</div>
Expand Down
51 changes: 35 additions & 16 deletions src/components/app-drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { Empty, Grid, Input, Radio, Tag } from 'antd';
import type { ReactNode } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
cn,
getManageAppDrawerCollapsed,
Expand Down Expand Up @@ -74,6 +75,7 @@ export function AppDrawer({
onSettings?: (app: AppDrawerItem) => void;
placement: Exclude<ManageAppDrawerPlacement, 'hidden'>;
}) {
const { t } = useTranslation();
const [query, setQuery] = useState('');
const normalizedQuery = query.trim().toLowerCase();
const filteredApps = useMemo(() => {
Expand Down Expand Up @@ -111,13 +113,15 @@ export function AppDrawer({
>
<div className="flex h-full flex-col">
<button
aria-label={collapsed ? '展开应用列表' : '收起应用列表'}
aria-label={
collapsed ? t('app_drawer.expand') : t('app_drawer.collapse')
}
className={cn(
'flex w-full items-center border-0 border-slate-100 border-b bg-transparent text-left transition-colors hover:bg-slate-50',
collapsed ? 'h-14 justify-center' : 'justify-between p-3',
)}
onClick={() => onCollapsedChange(!collapsed)}
title={collapsed ? '展开应用列表' : '收起应用列表'}
title={collapsed ? t('app_drawer.expand') : t('app_drawer.collapse')}
type="button"
>
{collapsed ? (
Expand All @@ -129,9 +133,13 @@ export function AppDrawer({
<AppstoreOutlined className="text-slate-500" />
</span>
<div className="min-w-0">
<div className="font-medium text-slate-900">应用列表</div>
<div className="font-medium text-slate-900">
{t('app_drawer.app_list')}
</div>
<div className="text-slate-500 text-xs">
共 {apps.length.toLocaleString()} 个应用
{t('app_drawer.apps_count', {
count: apps.length,
})}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
</div>
</div>
</div>
Expand All @@ -154,16 +162,22 @@ export function AppDrawer({
size="small"
value={placement}
>
<Radio.Button value="left">左侧</Radio.Button>
<Radio.Button value="right">右侧</Radio.Button>
<Radio.Button value="hidden">隐藏</Radio.Button>
<Radio.Button value="left">
{t('app_drawer.placement_left')}
</Radio.Button>
<Radio.Button value="right">
{t('app_drawer.placement_right')}
</Radio.Button>
<Radio.Button value="hidden">
{t('app_drawer.placement_hide')}
</Radio.Button>
</Radio.Group>
</div>
<div className="border-slate-100 border-b p-3">
<Input
allowClear
prefix={<SearchOutlined />}
placeholder="搜索应用"
placeholder={t('app_drawer.search_apps')}
value={query}
onChange={(event) => setQuery(event.target.value)}
/>
Expand Down Expand Up @@ -199,7 +213,7 @@ export function AppDrawer({
!isLoading && (
<Empty
className="my-8"
description="没有匹配的应用"
description={t('app_drawer.no_matching_apps')}
image={Empty.PRESENTED_IMAGE_SIMPLE}
/>
)
Expand Down Expand Up @@ -330,14 +344,15 @@ function AppIconButton({
isActive: boolean;
onSelect: (app: AppDrawerItem) => void;
}) {
const { t } = useTranslation();
return (
<button
className={cn(
'flex h-11 w-11 shrink-0 cursor-pointer items-center justify-center rounded-xl border-0 bg-transparent transition-colors hover:bg-slate-100',
isActive ? 'bg-blue-600 text-white hover:bg-blue-600' : undefined,
)}
onClick={() => onSelect(app)}
title={`${app.name} · ${formatCheckCount(app)}`}
title={`${app.name} · ${formatCheckCount(app, t)}`}
type="button"
>
<PlatformIcon platform={app.platform} />
Expand All @@ -356,6 +371,7 @@ function AppDrawerRow({
onSelect: (app: AppDrawerItem) => void;
onSettings?: (app: AppDrawerItem) => void;
}) {
const { t } = useTranslation();
return (
<div
aria-current={isActive ? 'page' : undefined}
Expand Down Expand Up @@ -386,7 +402,7 @@ function AppDrawerRow({
{app.name}
</span>
{app.status === 'paused' && (
<Tag className="m-0 shrink-0">暂停</Tag>
<Tag className="m-0 shrink-0">{t('app_drawer.paused')}</Tag>
)}
</span>
<span
Expand All @@ -395,19 +411,19 @@ function AppDrawerRow({
isActive ? 'text-blue-700' : 'text-slate-500',
)}
>
{formatCheckCount(app)}
{formatCheckCount(app, t)}
</span>
</span>
</button>
{onSettings && (
<button
aria-label={`打开 ${app.name} 应用设置`}
aria-label={t('app_drawer.open_app_settings', { name: app.name })}
className={cn(
'mr-2 flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center rounded-md border-0 bg-transparent text-slate-400 opacity-0 transition-all hover:bg-white/80 hover:text-blue-600 hover:opacity-100 focus-visible:opacity-100 group-hover:opacity-100',
isActive ? 'text-blue-600 hover:bg-blue-50' : undefined,
)}
onClick={() => onSettings(app)}
title="应用设置"
title={t('app_drawer.app_settings')}
type="button"
>
<SettingOutlined />
Expand All @@ -417,6 +433,9 @@ function AppDrawerRow({
);
}

function formatCheckCount(app: AppDrawerItem) {
return `${(app.checkCount ?? 0).toLocaleString()} 次检查`;
function formatCheckCount(
app: AppDrawerItem,
t: (key: string, options?: Record<string, unknown>) => string,
) {
return t('app_drawer.check_count', { count: app.checkCount ?? 0 });
}
Loading