From 6e0a2912288404dca1b322dff22f20f48f112458 Mon Sep 17 00:00:00 2001 From: bonamin Date: Mon, 21 Sep 2026 18:31:10 +0300 Subject: [PATCH] feat(devices): add console-only display name (console_name) Lets staff set a friendly name for a device independent of whatever name the end user gave it in the app. console_name is never shown to app users and never synced from/to device_name; every display label across search, equipment/helpdesk name resolution, device search, and the Manage tab's issue linker now falls back through console_name -> device_name -> serial rather than device_name alone. Also includes an incidental one-line fix in devices/service.py: the nested-struct deep-merge in update_device() was missing the newly added device_health_settings key. Co-Authored-By: Claude Sonnet 5 --- backend/devices/service.py | 5 +++-- backend/equipment/service.py | 3 ++- backend/helpdesk/service.py | 3 ++- backend/search/router.py | 5 +++-- frontend/src/modals/bellcloud/devices/DeleteDeviceModal.jsx | 2 +- frontend/src/pages/bellcloud/devices/tabs/ManageTab.jsx | 4 ++-- frontend/src/pages/bellcloud/users/UserDetail.jsx | 4 ++-- frontend/src/pages/bellcloud/users/UserListCardView.jsx | 2 +- 8 files changed, 16 insertions(+), 12 deletions(-) diff --git a/backend/devices/service.py b/backend/devices/service.py index 283f3c2..b836e85 100644 --- a/backend/devices/service.py +++ b/backend/devices/service.py @@ -159,9 +159,10 @@ def list_devices( if search: search_lower = search.lower() name_match = search_lower in (device.device_name or "").lower() + console_name_match = search_lower in (device.console_name or "").lower() location_match = search_lower in (device.device_location or "").lower() sn_match = search_lower in (device.serial_number or "").lower() - if not (name_match or location_match or sn_match): + if not (name_match or console_name_match or location_match or sn_match): continue results.append(device) @@ -222,7 +223,7 @@ def update_device(device_doc_id: str, data: DeviceUpdate) -> DeviceInDB: # Deep-merge nested structs so unmentioned sub-fields are preserved existing = doc.to_dict() nested_keys = ( - "device_attributes", "device_subscription", "device_stats", + "device_attributes", "device_subscription", "device_stats", "device_health_settings", ) for key in nested_keys: if key in update_data and isinstance(existing.get(key), dict): diff --git a/backend/equipment/service.py b/backend/equipment/service.py index eee4608..9aa6596 100644 --- a/backend/equipment/service.py +++ b/backend/equipment/service.py @@ -48,7 +48,8 @@ def _resolve_names(db, device_id: str | None, user_id: str | None) -> tuple[str, if device_id and isinstance(device_id, str) and device_id.strip(): device_doc = db.collection("devices").document(device_id.strip()).get() if device_doc.exists: - device_name = device_doc.to_dict().get("device_name", "") + device_data = device_doc.to_dict() + device_name = device_data.get("console_name") or device_data.get("device_name", "") if user_id and isinstance(user_id, str) and user_id.strip(): user_doc = db.collection("users").document(user_id.strip()).get() diff --git a/backend/helpdesk/service.py b/backend/helpdesk/service.py index be0ffa9..1889dce 100644 --- a/backend/helpdesk/service.py +++ b/backend/helpdesk/service.py @@ -50,7 +50,8 @@ def _resolve_device_name(db, device_id: str) -> str: try: doc = db.collection("devices").document(device_id.strip()).get() if doc.exists: - return doc.to_dict().get("device_name", "") + data = doc.to_dict() + return data.get("console_name") or data.get("device_name", "") except Exception as e: print(f"[helpdesk] Error resolving device name: {e}") return "" diff --git a/backend/search/router.py b/backend/search/router.py index d572dbc..df89d46 100644 --- a/backend/search/router.py +++ b/backend/search/router.py @@ -26,8 +26,9 @@ def _search_devices(q: str) -> list[dict]: return [] out = [] for d in results[:LIMIT]: - label = d.device_name or d.serial_number or d.device_id or d.id - sublabel = d.serial_number if d.device_name else None + display_name = d.console_name or d.device_name + label = display_name or d.serial_number or d.device_id or d.id + sublabel = d.serial_number if display_name else None out.append({ "type": "device", "id": d.id, diff --git a/frontend/src/modals/bellcloud/devices/DeleteDeviceModal.jsx b/frontend/src/modals/bellcloud/devices/DeleteDeviceModal.jsx index c32597f..a276e2c 100644 --- a/frontend/src/modals/bellcloud/devices/DeleteDeviceModal.jsx +++ b/frontend/src/modals/bellcloud/devices/DeleteDeviceModal.jsx @@ -3,7 +3,7 @@ import Modal from '@/components/ui/Modal' import Button from '@/components/ui/Button' export default function DeleteDeviceModal({ device, onConfirm, onCancel, error }) { - const name = device?.device_name || 'this device' + const name = device?.console_name || device?.device_name || 'this device' const serial = device?.serial_number || device?.device_id return ( diff --git a/frontend/src/pages/bellcloud/devices/tabs/ManageTab.jsx b/frontend/src/pages/bellcloud/devices/tabs/ManageTab.jsx index eb6d5d2..23e5e25 100644 --- a/frontend/src/pages/bellcloud/devices/tabs/ManageTab.jsx +++ b/frontend/src/pages/bellcloud/devices/tabs/ManageTab.jsx @@ -632,9 +632,9 @@ export default function ManageTab({ device, canEdit, deviceUsers: propUsers, use defaultType="issue" prefilledLinks={issueModal.entry ? undefined - : [{ entity_type: 'device', entity_id: id, display_name: device?.device_name || sn, locked: true }] + : [{ entity_type: 'device', entity_id: id, display_name: device?.console_name || device?.device_name || sn, locked: true }] } - knownEntities={id ? { [id]: device?.device_name || sn } : undefined} + knownEntities={id ? { [id]: device?.console_name || device?.device_name || sn } : undefined} onClose={() => setIssueModal({ open: false, entry: null })} onSaved={handleIssueSaved} onDelete={canEdit ? (entryId) => { diff --git a/frontend/src/pages/bellcloud/users/UserDetail.jsx b/frontend/src/pages/bellcloud/users/UserDetail.jsx index 28246e8..178aaec 100644 --- a/frontend/src/pages/bellcloud/users/UserDetail.jsx +++ b/frontend/src/pages/bellcloud/users/UserDetail.jsx @@ -820,7 +820,7 @@ export default function UserDetail() { {availableDevices.map((d) => ( ))} @@ -848,7 +848,7 @@ export default function UserDetail() { onClick={() => navigate(`/devices/${device.id}`)} style={{ background: 'none', border: 'none', cursor: 'pointer', padding: 0, fontSize: 'var(--font-size-sm)', fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-text-accent)', textAlign: 'left', display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '160px' }} > - {device.device_name || 'Unnamed Device'} + {device.console_name || device.device_name || 'Unnamed Device'}

{device.device_id || device.id} diff --git a/frontend/src/pages/bellcloud/users/UserListCardView.jsx b/frontend/src/pages/bellcloud/users/UserListCardView.jsx index a9b3ba8..59e123d 100644 --- a/frontend/src/pages/bellcloud/users/UserListCardView.jsx +++ b/frontend/src/pages/bellcloud/users/UserListCardView.jsx @@ -405,7 +405,7 @@ function LinkedDevicesModal({ userId, userName, open, onClose }) {

- {d.device_name || d.device_id || d.id} + {d.console_name || d.device_name || d.device_id || d.id}
{d.device_id && (