mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-15 03:57:30 +08:00
webui i18n: localize EKG panel labels and actions (zh/en)
This commit is contained in:
@@ -20,6 +20,14 @@ const resources = {
|
|||||||
sidebarOps: 'Operations',
|
sidebarOps: 'Operations',
|
||||||
sidebarInsights: 'Insights',
|
sidebarInsights: 'Insights',
|
||||||
ekg: 'EKG',
|
ekg: 'EKG',
|
||||||
|
ekgEscalations: 'Escalations',
|
||||||
|
ekgSourceStats: 'Source Stats',
|
||||||
|
ekgChannelStats: 'Channel Stats',
|
||||||
|
ekgTopProvidersWorkload: 'Top Providers (workload)',
|
||||||
|
ekgTopProvidersAll: 'Top Providers (all)',
|
||||||
|
ekgTopErrsigWorkload: 'Top Error Signatures (workload)',
|
||||||
|
ekgTopErrsigHeartbeat: 'Top Error Signatures (heartbeat)',
|
||||||
|
ekgTopErrsigAll: 'Top Error Signatures (all)',
|
||||||
taskList: 'Task List',
|
taskList: 'Task List',
|
||||||
taskDetail: 'Task Detail',
|
taskDetail: 'Task Detail',
|
||||||
taskQueue: 'Task Queue',
|
taskQueue: 'Task Queue',
|
||||||
@@ -195,6 +203,14 @@ const resources = {
|
|||||||
sidebarOps: '运维',
|
sidebarOps: '运维',
|
||||||
sidebarInsights: '洞察',
|
sidebarInsights: '洞察',
|
||||||
ekg: 'EKG',
|
ekg: 'EKG',
|
||||||
|
ekgEscalations: '升级拦截次数',
|
||||||
|
ekgSourceStats: '来源统计',
|
||||||
|
ekgChannelStats: '通道统计',
|
||||||
|
ekgTopProvidersWorkload: 'Top Providers(业务负载)',
|
||||||
|
ekgTopProvidersAll: 'Top Providers(全量)',
|
||||||
|
ekgTopErrsigWorkload: 'Top 错误签名(业务负载)',
|
||||||
|
ekgTopErrsigHeartbeat: 'Top 错误签名(心跳)',
|
||||||
|
ekgTopErrsigAll: 'Top 错误签名(全量)',
|
||||||
taskList: '任务列表',
|
taskList: '任务列表',
|
||||||
taskDetail: '任务详情',
|
taskDetail: '任务详情',
|
||||||
taskQueue: '任务队列',
|
taskQueue: '任务队列',
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useAppContext } from '../context/AppContext';
|
import { useAppContext } from '../context/AppContext';
|
||||||
|
|
||||||
type EKGKV = { key?: string; score?: number; count?: number };
|
type EKGKV = { key?: string; score?: number; count?: number };
|
||||||
|
|
||||||
const EKG: React.FC = () => {
|
const EKG: React.FC = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const { q } = useAppContext();
|
const { q } = useAppContext();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [ekgWindow, setEkgWindow] = useState<'6h' | '24h' | '7d'>(() => {
|
const [ekgWindow, setEkgWindow] = useState<'6h' | '24h' | '7d'>(() => {
|
||||||
@@ -56,47 +58,47 @@ const EKG: React.FC = () => {
|
|||||||
<option value="24h">24h</option>
|
<option value="24h">24h</option>
|
||||||
<option value="7d">7d</option>
|
<option value="7d">7d</option>
|
||||||
</select>
|
</select>
|
||||||
<button onClick={fetchData} className="px-3 py-1.5 rounded-lg bg-zinc-800 hover:bg-zinc-700 text-sm">{loading ? 'Loading...' : 'Refresh'}</button>
|
<button onClick={fetchData} className="px-3 py-1.5 rounded-lg bg-zinc-800 hover:bg-zinc-700 text-sm">{loading ? t('loading') : t('refresh')}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 xl:grid-cols-3 gap-3 text-xs">
|
<div className="grid grid-cols-1 xl:grid-cols-3 gap-3 text-xs">
|
||||||
<div className="rounded-xl border border-zinc-800 bg-zinc-900/40 p-3">
|
<div className="rounded-xl border border-zinc-800 bg-zinc-900/40 p-3">
|
||||||
<div className="text-zinc-500 mb-1">Escalations</div>
|
<div className="text-zinc-500 mb-1">{t('ekgEscalations')}</div>
|
||||||
<div className="text-zinc-100 text-2xl font-semibold">{escalationCount}</div>
|
<div className="text-zinc-100 text-2xl font-semibold">{escalationCount}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-xl border border-zinc-800 bg-zinc-900/40 p-3">
|
<div className="rounded-xl border border-zinc-800 bg-zinc-900/40 p-3">
|
||||||
<div className="text-zinc-500 mb-1">Source Stats</div>
|
<div className="text-zinc-500 mb-1">{t('ekgSourceStats')}</div>
|
||||||
<div className="space-y-1">{Object.keys(sourceStats).length === 0 ? <div className="text-zinc-500">-</div> : Object.entries(sourceStats).map(([k,v]) => <div key={k} className="text-zinc-200">{k}: <span className="text-zinc-400">{v}</span></div>)}</div>
|
<div className="space-y-1">{Object.keys(sourceStats).length === 0 ? <div className="text-zinc-500">-</div> : Object.entries(sourceStats).map(([k,v]) => <div key={k} className="text-zinc-200">{k}: <span className="text-zinc-400">{v}</span></div>)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-xl border border-zinc-800 bg-zinc-900/40 p-3">
|
<div className="rounded-xl border border-zinc-800 bg-zinc-900/40 p-3">
|
||||||
<div className="text-zinc-500 mb-1">Channel Stats</div>
|
<div className="text-zinc-500 mb-1">{t('ekgChannelStats')}</div>
|
||||||
<div className="space-y-1">{Object.keys(channelStats).length === 0 ? <div className="text-zinc-500">-</div> : Object.entries(channelStats).map(([k,v]) => <div key={k} className="text-zinc-200">{k}: <span className="text-zinc-400">{v}</span></div>)}</div>
|
<div className="space-y-1">{Object.keys(channelStats).length === 0 ? <div className="text-zinc-500">-</div> : Object.entries(channelStats).map(([k,v]) => <div key={k} className="text-zinc-200">{k}: <span className="text-zinc-400">{v}</span></div>)}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 xl:grid-cols-2 gap-3 text-xs">
|
<div className="grid grid-cols-1 xl:grid-cols-2 gap-3 text-xs">
|
||||||
<div className="rounded-xl border border-zinc-800 bg-zinc-900/40 p-3">
|
<div className="rounded-xl border border-zinc-800 bg-zinc-900/40 p-3">
|
||||||
<div className="text-zinc-500 mb-1">Top Providers (workload)</div>
|
<div className="text-zinc-500 mb-1">{t('ekgTopProvidersWorkload')}</div>
|
||||||
<div className="space-y-1">{providerTopWorkload.length === 0 ? <div className="text-zinc-500">-</div> : providerTopWorkload.map((x,i)=><div key={i} className="text-zinc-200">{x.key} <span className="text-zinc-500">({Number(x.score||0).toFixed(2)})</span></div>)}</div>
|
<div className="space-y-1">{providerTopWorkload.length === 0 ? <div className="text-zinc-500">-</div> : providerTopWorkload.map((x,i)=><div key={i} className="text-zinc-200">{x.key} <span className="text-zinc-500">({Number(x.score||0).toFixed(2)})</span></div>)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-xl border border-zinc-800 bg-zinc-900/40 p-3">
|
<div className="rounded-xl border border-zinc-800 bg-zinc-900/40 p-3">
|
||||||
<div className="text-zinc-500 mb-1">Top Providers (all)</div>
|
<div className="text-zinc-500 mb-1">{t('ekgTopProvidersAll')}</div>
|
||||||
<div className="space-y-1">{providerTop.length === 0 ? <div className="text-zinc-500">-</div> : providerTop.map((x,i)=><div key={i} className="text-zinc-200">{x.key} <span className="text-zinc-500">({Number(x.score||0).toFixed(2)})</span></div>)}</div>
|
<div className="space-y-1">{providerTop.length === 0 ? <div className="text-zinc-500">-</div> : providerTop.map((x,i)=><div key={i} className="text-zinc-200">{x.key} <span className="text-zinc-500">({Number(x.score||0).toFixed(2)})</span></div>)}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 xl:grid-cols-3 gap-3 text-xs flex-1 min-h-0">
|
<div className="grid grid-cols-1 xl:grid-cols-3 gap-3 text-xs flex-1 min-h-0">
|
||||||
<div className="rounded-xl border border-zinc-800 bg-zinc-900/40 p-3 overflow-y-auto">
|
<div className="rounded-xl border border-zinc-800 bg-zinc-900/40 p-3 overflow-y-auto">
|
||||||
<div className="text-zinc-500 mb-1">Top Error Signatures (workload)</div>
|
<div className="text-zinc-500 mb-1">{t('ekgTopErrsigWorkload')}</div>
|
||||||
<div className="space-y-1">{errsigTopWorkload.length === 0 ? <div className="text-zinc-500">-</div> : errsigTopWorkload.map((x,i)=><div key={i} className="text-zinc-200 truncate">{x.key} <span className="text-zinc-500">(x{x.count||0})</span></div>)}</div>
|
<div className="space-y-1">{errsigTopWorkload.length === 0 ? <div className="text-zinc-500">-</div> : errsigTopWorkload.map((x,i)=><div key={i} className="text-zinc-200 truncate">{x.key} <span className="text-zinc-500">(x{x.count||0})</span></div>)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-xl border border-zinc-800 bg-zinc-900/40 p-3 overflow-y-auto">
|
<div className="rounded-xl border border-zinc-800 bg-zinc-900/40 p-3 overflow-y-auto">
|
||||||
<div className="text-zinc-500 mb-1">Top Error Signatures (heartbeat)</div>
|
<div className="text-zinc-500 mb-1">{t('ekgTopErrsigHeartbeat')}</div>
|
||||||
<div className="space-y-1">{errsigTopHeartbeat.length === 0 ? <div className="text-zinc-500">-</div> : errsigTopHeartbeat.map((x,i)=><div key={i} className="text-zinc-200 truncate">{x.key} <span className="text-zinc-500">(x{x.count||0})</span></div>)}</div>
|
<div className="space-y-1">{errsigTopHeartbeat.length === 0 ? <div className="text-zinc-500">-</div> : errsigTopHeartbeat.map((x,i)=><div key={i} className="text-zinc-200 truncate">{x.key} <span className="text-zinc-500">(x{x.count||0})</span></div>)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-xl border border-zinc-800 bg-zinc-900/40 p-3 overflow-y-auto">
|
<div className="rounded-xl border border-zinc-800 bg-zinc-900/40 p-3 overflow-y-auto">
|
||||||
<div className="text-zinc-500 mb-1">Top Error Signatures (all)</div>
|
<div className="text-zinc-500 mb-1">{t('ekgTopErrsigAll')}</div>
|
||||||
<div className="space-y-1">{errsigTop.length === 0 ? <div className="text-zinc-500">-</div> : errsigTop.map((x,i)=><div key={i} className="text-zinc-200 truncate">{x.key} <span className="text-zinc-500">(x{x.count||0})</span></div>)}</div>
|
<div className="space-y-1">{errsigTop.length === 0 ? <div className="text-zinc-500">-</div> : errsigTop.map((x,i)=><div key={i} className="text-zinc-200 truncate">{x.key} <span className="text-zinc-500">(x{x.count||0})</span></div>)}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user