Files
awoooi/apps/web/src/components/dashboard/connection-status.tsx
OG T 196d269b92 feat: add all application source code
- apps/api: FastAPI backend with Dockerfile
- apps/web: Next.js frontend with Dockerfile
- apps/sensor: Signal collection agent
- packages: shared packages

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-22 18:57:44 +08:00

136 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
/**
* ConnectionStatus - SSE 連線狀態指示器
* =====================================
* 顯示與後端 SSE 的連線狀態
*
* i18n: 100% 使用 useTranslations禁止任何寫死字串
* 符合 AWOOOI 專案開發憲法 v2.0
*
* States:
* - disconnected: 灰色
* - connecting: 藍色 pulse
* - connected: 綠色
* - reconnecting: 橘色 pulse
* - error: 紅色
*/
import { useTranslations } from 'next-intl'
import { useLocale } from 'next-intl'
import { StatusOrb, type StatusType } from '@/components/ui/status-orb'
import { useConnectionStatus, useMockMode, useLastUpdate } from '@/stores/dashboard.store'
import { cn } from '@/lib/utils'
// =============================================================================
// Status Mapping
// =============================================================================
type ConnectionStatusType = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error'
const connectionToStatus: Record<ConnectionStatusType, StatusType> = {
disconnected: 'idle',
connecting: 'syncing',
connected: 'healthy',
reconnecting: 'warning',
error: 'critical',
}
// i18n key mapping (不再使用 hardcoded labels)
const connectionLabelKeys: Record<ConnectionStatusType, string> = {
disconnected: 'disconnected',
connecting: 'connecting',
connected: 'connected',
reconnecting: 'reconnecting',
error: 'error',
}
// =============================================================================
// Component
// =============================================================================
interface ConnectionStatusProps {
className?: string
showLabel?: boolean
showLastUpdate?: boolean
}
export function ConnectionStatus({
className,
showLabel = true,
showLastUpdate = false,
}: ConnectionStatusProps) {
const t = useTranslations('connection')
const tCommon = useTranslations('common')
const locale = useLocale()
const connectionStatus = useConnectionStatus()
const mockMode = useMockMode()
const lastUpdate = useLastUpdate()
const status = connectionToStatus[connectionStatus]
const labelKey = connectionLabelKeys[connectionStatus]
// Format last update time with dynamic locale
const lastUpdateStr = lastUpdate
? lastUpdate.toLocaleTimeString(locale === 'zh-TW' ? 'zh-TW' : 'en-US', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
})
: '--:--:--'
return (
<div className={cn('flex items-center gap-2', className)}>
<StatusOrb
status={status}
size="sm"
pulse={connectionStatus === 'connecting' || connectionStatus === 'reconnecting'}
glow={connectionStatus === 'connected'}
/>
{showLabel && (
<div className="flex flex-col">
<span className="text-xs font-mono text-nothing-gray-600">
{t(labelKey)}
</span>
{showLastUpdate && lastUpdate && (
<span className="text-[10px] font-mono text-nothing-gray-400">
{lastUpdateStr}
</span>
)}
</div>
)}
{mockMode && (
<span className="px-1.5 py-0.5 text-[10px] font-mono bg-status-warning/10 text-status-warning rounded">
{t('mockMode')}
</span>
)}
</div>
)
}
// =============================================================================
// Compact Version (for header)
// =============================================================================
export function ConnectionStatusCompact({ className }: { className?: string }) {
const t = useTranslations('connection')
const connectionStatus = useConnectionStatus()
const status = connectionToStatus[connectionStatus]
return (
<div
className={cn(
'flex items-center gap-2 px-3 py-1.5 rounded-lg bg-nothing-gray-100',
className
)}
>
<StatusOrb status={status} size="sm" pulse={connectionStatus !== 'connected'} />
<span className="font-mono text-xs text-nothing-gray-600 uppercase">
{t(connectionLabelKeys[connectionStatus])}
</span>
</div>
)
}