fix: resolve camera settings click and subscription API errors
- Camera settings: Use UUID `id` instead of optional `deviceId` for camera selection - Camera settings: Only fetch history when deviceId exists - Camera settings: Fix location display for JSONB location object - Subscription: Safely handle empty JSON response when user has no subscription 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
95353ae2d7
commit
1c272140d9
@ -46,25 +46,28 @@ export default function CameraSettingsPage() {
|
|||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
// 获取相机详细信息
|
// 使用 id 查找相机
|
||||||
const camera = cameras.find(c => c.deviceId === cameraId);
|
const camera = cameras.find(c => c.id === cameraId);
|
||||||
if (camera) {
|
if (camera) {
|
||||||
setCameraDetails(camera);
|
setCameraDetails(camera);
|
||||||
setSelectedCamera(cameraId);
|
setSelectedCamera(cameraId);
|
||||||
|
|
||||||
// 获取历史数据
|
// 只有当 deviceId 存在时才获取历史数据
|
||||||
const historyData = await cameraService.getCameraHistory(cameraId);
|
if (camera.deviceId) {
|
||||||
const formattedHistory: CameraHistoryRecord[] = historyData.map(item => ({
|
const historyData = await cameraService.getCameraHistory(camera.deviceId);
|
||||||
date: new Date(item.time).toLocaleString(),
|
const formattedHistory: CameraHistoryRecord[] = historyData.map(item => ({
|
||||||
status: 'active' as const, // API doesn't provide status in history, defaulting to active
|
date: new Date(item.time).toLocaleString(),
|
||||||
temperature: item.temperature,
|
status: 'active' as const, // API doesn't provide status in history, defaulting to active
|
||||||
coolerPower: item.coolerPower,
|
temperature: item.temperature,
|
||||||
gain: item.gain,
|
coolerPower: item.coolerPower,
|
||||||
exposureCount: undefined,
|
gain: item.gain,
|
||||||
uptime: undefined
|
exposureCount: undefined,
|
||||||
}));
|
uptime: undefined
|
||||||
|
}));
|
||||||
setCameraHistory(formattedHistory);
|
setCameraHistory(formattedHistory);
|
||||||
|
} else {
|
||||||
|
setCameraHistory([]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取相机详细信息失败:', error);
|
console.error('获取相机详细信息失败:', error);
|
||||||
@ -160,7 +163,7 @@ export default function CameraSettingsPage() {
|
|||||||
<div
|
<div
|
||||||
key={camera.id}
|
key={camera.id}
|
||||||
className="bg-white dark:bg-gray-800 rounded-lg p-4 sm:p-5 border border-gray-200 dark:border-gray-700 cursor-pointer hover:shadow-lg transition-all duration-200 hover:scale-105 min-w-[280px] w-full"
|
className="bg-white dark:bg-gray-800 rounded-lg p-4 sm:p-5 border border-gray-200 dark:border-gray-700 cursor-pointer hover:shadow-lg transition-all duration-200 hover:scale-105 min-w-[280px] w-full"
|
||||||
onClick={() => handleSelectCamera(camera.deviceId)}
|
onClick={() => handleSelectCamera(camera.id)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<div className="flex items-center min-w-0 flex-1">
|
<div className="flex items-center min-w-0 flex-1">
|
||||||
@ -175,7 +178,7 @@ export default function CameraSettingsPage() {
|
|||||||
<div className="space-y-2.5">
|
<div className="space-y-2.5">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<span className="text-sm text-gray-500 dark:text-gray-400 flex-shrink-0">位置:</span>
|
<span className="text-sm text-gray-500 dark:text-gray-400 flex-shrink-0">位置:</span>
|
||||||
<span className="text-sm font-medium text-gray-900 dark:text-white truncate ml-2">{camera.location}</span>
|
<span className="text-sm font-medium text-gray-900 dark:text-white truncate ml-2">{camera.location?.site_name || camera.legacyLocation || '未知'}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<span className="text-sm text-gray-500 dark:text-gray-400 flex-shrink-0">温度:</span>
|
<span className="text-sm text-gray-500 dark:text-gray-400 flex-shrink-0">温度:</span>
|
||||||
@ -233,7 +236,7 @@ export default function CameraSettingsPage() {
|
|||||||
<Monitor className="text-blue-500 mr-3" size={32} />
|
<Monitor className="text-blue-500 mr-3" size={32} />
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-2xl font-bold text-gray-900 dark:text-white">{cameraDetails.name}</h3>
|
<h3 className="text-2xl font-bold text-gray-900 dark:text-white">{cameraDetails.name}</h3>
|
||||||
<p className="text-gray-500 dark:text-gray-400">{cameraDetails.location}</p>
|
<p className="text-gray-500 dark:text-gray-400">{cameraDetails.location?.site_name || cameraDetails.legacyLocation || '未知'}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className={`px-3 py-2 rounded-full text-sm font-medium ${getStatusColor(cameraDetails.status)}`}>
|
<span className={`px-3 py-2 rounded-full text-sm font-medium ${getStatusColor(cameraDetails.status)}`}>
|
||||||
|
|||||||
@ -32,7 +32,7 @@ export interface CheckoutSessionResponse {
|
|||||||
|
|
||||||
// New interfaces for the enhanced subscription API
|
// New interfaces for the enhanced subscription API
|
||||||
export interface SubscriptionPlan {
|
export interface SubscriptionPlan {
|
||||||
id: number;
|
id: string;
|
||||||
planId: string;
|
planId: string;
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
@ -49,9 +49,9 @@ export interface SubscriptionPlan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface UserSubscription {
|
export interface UserSubscription {
|
||||||
id: number;
|
id: string;
|
||||||
userProfileId: string;
|
userProfileId: string;
|
||||||
subscriptionPlanId: number;
|
subscriptionPlanId: string;
|
||||||
subscriptionPlan: SubscriptionPlan;
|
subscriptionPlan: SubscriptionPlan;
|
||||||
stripeSubscriptionId?: string;
|
stripeSubscriptionId?: string;
|
||||||
status: 'active' | 'canceled' | 'past_due' | 'trialing' | 'incomplete';
|
status: 'active' | 'canceled' | 'past_due' | 'trialing' | 'incomplete';
|
||||||
@ -213,7 +213,13 @@ export const subscriptionApi = {
|
|||||||
throw new Error(`Failed to fetch user subscription: ${response.statusText}`)
|
throw new Error(`Failed to fetch user subscription: ${response.statusText}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.json()
|
// Safely handle empty response body
|
||||||
|
const text = await response.text()
|
||||||
|
if (!text || text.trim() === '' || text === 'null') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON.parse(text)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching user subscription:', error)
|
console.error('Error fetching user subscription:', error)
|
||||||
return null
|
return null
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user