jim800121chen ebe86663b1 fix(local-tool): i18n type error — 用 devices.status.connecting/disconnecting
devices.connecting / devices.disconnecting 不在 TranslationDict type 裡,
改用已存在的 devices.status.connecting + 新增 devices.status.disconnecting。
刪掉上一個 commit 多加的頂層 devices.connecting / disconnecting key。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 19:11:36 +08:00

92 lines
3.6 KiB
TypeScript

'use client';
import Link from 'next/link';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Loader2 } from 'lucide-react';
import { DeviceStatusBadge } from './device-status';
import { useDeviceStore } from '@/stores/device-store';
import { useDevicePreferencesStore } from '@/stores/device-preferences-store';
import { useTranslation } from '@/lib/i18n';
import type { Device } from '@/types/device';
interface DeviceCardProps {
device: Device;
isFirstCard?: boolean;
}
export function DeviceCard({ device, isFirstCard }: DeviceCardProps) {
const { t } = useTranslation();
const { connectDevice, disconnectDevice, connectingId, disconnectingId } = useDeviceStore();
const prefs = useDevicePreferencesStore((s) => s.getPreferences(device.id));
const displayName = prefs.alias || device.name;
const isConnected = device.status === 'connected' || device.status === 'flashing' || device.status === 'inferencing';
const isConnecting = connectingId === device.id;
const isDisconnecting = disconnectingId === device.id;
const isBusy = isConnecting || isDisconnecting || connectingId !== null || disconnectingId !== null;
return (
<Card>
<CardHeader className="pb-3">
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<CardTitle className="text-base truncate">{displayName}</CardTitle>
{prefs.alias && (
<p className="text-xs text-muted-foreground">{device.name}</p>
)}
</div>
<DeviceStatusBadge status={isConnecting ? 'connecting' : device.status} />
</div>
</CardHeader>
<CardContent className="space-y-3">
<div className="grid grid-cols-2 gap-2 text-sm">
<div>
<p className="text-muted-foreground">{t('devices.type')}</p>
<p className="font-medium">{device.type}</p>
</div>
<div>
<p className="text-muted-foreground">{t('devices.firmware')}</p>
<p className="font-medium">{device.firmwareVersion || t('common.na')}</p>
</div>
{device.flashedModel && (
<div className="col-span-2">
<p className="text-muted-foreground">{t('devices.flashedModel')}</p>
<p className="font-medium">{device.flashedModel}</p>
</div>
)}
</div>
<div className="flex gap-2">
{isConnected ? (
<>
<Link href={`/devices/${device.id}`}>
<Button size="sm" variant="outline" disabled={isBusy} {...(isFirstCard ? { 'data-tour-id': 'manage-device-btn' } : {})}>
{t('common.manage')}
</Button>
</Link>
<Button
size="sm"
variant="ghost"
disabled={isBusy}
onClick={() => disconnectDevice(device.id)}
>
{isDisconnecting && <Loader2 className="mr-1 h-3 w-3 animate-spin" />}
{isDisconnecting ? t('devices.status.disconnecting') : t('common.disconnect')}
</Button>
</>
) : (
<Button
size="sm"
disabled={isBusy}
onClick={() => connectDevice(device.id)}
{...(isFirstCard ? { 'data-tour-id': 'connect-device-btn' } : {})}
>
{isConnecting && <Loader2 className="mr-1 h-3 w-3 animate-spin" />}
{isConnecting ? t('devices.status.connecting') : t('common.connect')}
</Button>
)}
</div>
</CardContent>
</Card>
);
}