fix(api): Convert location IDs to Legacy API numeric codes

- Add legacyCode to ROOM_LOCATIONS constants (102-200)
- Add getLocationLegacyCode() to convert ID -> code when saving
- Add getLocationIdFromCode() to convert code -> ID when loading
- updateDeviceMetadata now sends numeric codes to Legacy API
- getDevicesForBeneficiary now converts codes back to string IDs

Legacy API expects numeric location codes (e.g., 102 for Bedroom),
but frontend uses string IDs (e.g., 'bedroom'). This fix ensures
proper bidirectional conversion.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Sergei 2026-01-24 14:17:46 -08:00
parent 197a269d10
commit 2aff43af34

View File

@ -30,20 +30,32 @@ const ELDERLY_AVATARS = [
// Room locations for sensor placement // Room locations for sensor placement
// Used in device settings to select where sensor is installed // Used in device settings to select where sensor is installed
export const ROOM_LOCATIONS = [ export const ROOM_LOCATIONS = [
{ id: 'bedroom', label: 'Bedroom', icon: '🛏️' }, { id: 'bedroom', label: 'Bedroom', icon: '🛏️', legacyCode: 102 },
{ id: 'living_room', label: 'Living Room', icon: '🛋️' }, { id: 'living_room', label: 'Living Room', icon: '🛋️', legacyCode: 103 },
{ id: 'kitchen', label: 'Kitchen', icon: '🍳' }, { id: 'kitchen', label: 'Kitchen', icon: '🍳', legacyCode: 104 },
{ id: 'bathroom', label: 'Bathroom', icon: '🚿' }, { id: 'bathroom', label: 'Bathroom', icon: '🚿', legacyCode: 105 },
{ id: 'hallway', label: 'Hallway', icon: '🚪' }, { id: 'hallway', label: 'Hallway', icon: '🚪', legacyCode: 106 },
{ id: 'entrance', label: 'Entrance', icon: '🏠' }, { id: 'entrance', label: 'Entrance', icon: '🏠', legacyCode: 111 },
{ id: 'garage', label: 'Garage', icon: '🚗' }, { id: 'garage', label: 'Garage', icon: '🚗', legacyCode: 108 },
{ id: 'basement', label: 'Basement', icon: '🪜' }, { id: 'basement', label: 'Basement', icon: '🪜', legacyCode: 110 },
{ id: 'office', label: 'Office', icon: '💼' }, { id: 'office', label: 'Office', icon: '💼', legacyCode: 107 },
{ id: 'other', label: 'Other', icon: '📍' }, { id: 'other', label: 'Other', icon: '📍', legacyCode: 200 },
] as const; ] as const;
export type RoomLocationId = typeof ROOM_LOCATIONS[number]['id']; export type RoomLocationId = typeof ROOM_LOCATIONS[number]['id'];
// Helper to convert location ID to Legacy API code
function getLocationLegacyCode(locationId: string): number | undefined {
const location = ROOM_LOCATIONS.find(loc => loc.id === locationId);
return location?.legacyCode;
}
// Helper to convert Legacy API code to location ID
export function getLocationIdFromCode(code: number): RoomLocationId | undefined {
const location = ROOM_LOCATIONS.find(loc => loc.legacyCode === code);
return location?.id;
}
// Get consistent avatar based on deployment_id // Get consistent avatar based on deployment_id
function getAvatarForBeneficiary(deploymentId: number): string { function getAvatarForBeneficiary(deploymentId: number): string {
const index = deploymentId % ELDERLY_AVATARS.length; const index = deploymentId % ELDERLY_AVATARS.length;
@ -1646,6 +1658,19 @@ class ApiService {
status = 'offline'; // 🔴 Definitely problem status = 'offline'; // 🔴 Definitely problem
} }
// Convert numeric location code to string ID if needed
let locationId = '';
if (location) {
const numericLocation = parseInt(location, 10);
if (!isNaN(numericLocation)) {
// It's a numeric code from Legacy API - convert to our ID
locationId = getLocationIdFromCode(numericLocation) || '';
} else {
// It's already a string (legacy data or custom location)
locationId = location;
}
}
return { return {
deviceId: deviceId.toString(), deviceId: deviceId.toString(),
wellId: parseInt(wellId, 10), wellId: parseInt(wellId, 10),
@ -1653,7 +1678,7 @@ class ApiService {
name: `WP_${wellId}_${mac.slice(-6).toLowerCase()}`, name: `WP_${wellId}_${mac.slice(-6).toLowerCase()}`,
status: status, status: status,
lastSeen: lastSeen, lastSeen: lastSeen,
location: location || '', location: locationId,
description: description || '', description: description || '',
beneficiaryId: beneficiaryId, beneficiaryId: beneficiaryId,
deploymentId: deploymentId, deploymentId: deploymentId,
@ -1779,7 +1804,7 @@ class ApiService {
async updateDeviceMetadata( async updateDeviceMetadata(
deviceId: string, deviceId: string,
updates: { updates: {
location?: string; location?: string; // Location ID (e.g., 'bedroom', 'kitchen') - will be converted to Legacy API code
description?: string; description?: string;
} }
): Promise<ApiResponse<{ success: boolean }>> { ): Promise<ApiResponse<{ success: boolean }>> {
@ -1797,8 +1822,15 @@ class ApiService {
}); });
// Add optional fields if provided // Add optional fields if provided
// Location must be converted from ID to Legacy API numeric code
if (updates.location !== undefined) { if (updates.location !== undefined) {
formData.append('location', updates.location); const legacyCode = getLocationLegacyCode(updates.location);
if (legacyCode !== undefined) {
formData.append('location', legacyCode.toString());
} else {
// If location ID not found, log warning but don't fail
console.warn(`Unknown location ID: ${updates.location}, skipping location update`);
}
} }
if (updates.description !== undefined) { if (updates.description !== undefined) {
formData.append('description', updates.description); formData.append('description', updates.description);