-
-
Notifications
You must be signed in to change notification settings - Fork 0
Device Detail Alias UI Sync
Documentação técnica da correção de feedback visual ao editar o nome (alias) do device na tela /device-detail (Manager).
Issue de origem: app-community#382.
Na tela de detalhe de dispositivo (/device-detail):
- O usuário edita o nome do device e grava.
- A alteração é persistida no backend (API
devices). - A UI não refletia o novo nome até um refresh (F5) da página.
O feedback esperado é imediato: após save com sucesso, o valor exibido no header deve ser o novo alias sem reload.
| Visão | O que este fluxo faz | O que não faz |
|---|---|---|
| MANAGER | Rota principal: lista Dispositivos → detalhe → editar alias → save; header atualiza na hora | Não altera permissões, tipos de device nem DeviceConfig de PDV/DISPLAY |
| ADMIN | Pode reutilizar a mesma página compartilhada de ui-common quando a visão importa DeviceDetailPage
|
Não é o alvo prioritário do smoke Manager |
| POS / PPC / SHOP / DELIVERY / CRM | Não expõem esta tela de edição de alias de device | Não devem assumir ownership da tela de device-detail |
Fronteiras: módulo compartilhado ui-common; rota consumida pelo Manager via ui-manager.
| Peça | Caminho |
|---|---|
| Shell da página |
ui-common → src/react/pages/DeviceDetailPage.js (orquestra hooks/seções; ≤ 500 linhas após modularização) |
| Header (alias edit/save) | src/react/pages/Devices/detail/DeviceDetailHeader.js |
Ações (inclui saveAlias) |
src/react/pages/Devices/detail/useDeviceDetailActions.js |
| Estado local + ref de skip |
src/react/pages/Devices/detail/useDeviceDetailStateA.js (ou estado co-localizado nos hooks de detail) |
| Merge store pós-save |
src/react/utils/deviceAliasSync.js → buildDeviceAliasStoreUpdates
|
| Smoke browser | src/tests/browser/manager/device-detail-alias-save.spec.js |
| Unit | src/tests/react/utils/deviceAliasSync.test.js |
Rota Manager: ui-manager importa DeviceDetailPage de @controleonline/ui-common.
Havia descompasso entre:
-
Persistência (
deviceActions.save({ id, alias })) — ok no backend. -
Estado local (
alias/aliasInputno header). -
Store runtime (
deviceedevice_config), de ondeinitialAliasé derivado:
initialAlias = currentDevice?.alias
|| currentDeviceConfig?.device?.alias
|| currentDevice?.device
Um useEffect sincroniza o estado local a partir de initialAlias quando não está em modo edição:
useEffect(() => {
if (editingAlias) return;
if (skipAliasSyncFromStoreRef.current) {
skipAliasSyncFromStoreRef.current = false;
return;
}
setAlias(initialAlias || '');
setAliasInput(initialAlias || '');
}, [editingAlias, initialAlias]);
Se o save atualizava só o backend (ou só o state local) sem atualizar o store, na saída do modo edição o efeito reaplicava o initialAlias antigo → a UI “voltava” ao nome anterior até um reload completo recarregar o device do servidor.
Após deviceActions.save com sucesso:
- Calcular o alias definitivo (
savedDevice?.alias || trimmed). - Chamar
buildDeviceAliasStoreUpdates({ deviceId, nextAlias, runtimeDevice, runtimeDeviceConfig, savedDevice, normalizeEntityId }). - Persistir no store:
deviceActions.setItem(mergedDevice)-
deviceConfigActions.setItem(nextDeviceConfig)quando houver config aninhada comdevice.alias
- Armar
skipAliasSyncFromStoreRef.current = trueantes de atualizar o state local (evita race com ouseEffectno mesmo ciclo). -
setAlias(nextAlias),setAliasInput(nextAlias),setEditingAlias(false).
Responsável por manter device e device_config.device coerentes com o alias gravado, para que initialAlias derivado do store não “snap back” para o valor antigo.
- Mescla o device runtime (mesmo id) com o payload salvo e força
alias. - Se existir
runtimeDeviceConfig, clona e atualizadevice.aliasaninhado.
sequenceDiagram
participant U as Usuário (Manager)
participant H as DeviceDetailHeader
participant A as useDeviceDetailActions.saveAlias
participant API as deviceActions.save
participant S as Store (device + device_config)
participant E as useEffect (sync initialAlias)
U->>H: Edita alias + grava
H->>A: onSaveAlias
A->>API: save({ id, alias })
API-->>A: savedDevice
A->>S: setItem(mergedDevice / nextDeviceConfig)
A->>A: skipAliasSyncFromStoreRef = true
A->>H: setAlias + setAliasInput + exit edit
Note over E: effect roda com editingAlias=false
E->>E: vê skip=true → consome flag e não sobrescreve
H-->>U: Header mostra novo nome (sem F5)
- Em
/device-detail, após editar o nome e gravar com sucesso, o nome no header passa a ser o novo valor sem refresh. - Sem regressão em outros campos editáveis da mesma tela nem na listagem de dispositivos.
- Persistência no backend permanece correta.
- Evidência: smoke
device-detail-alias-save.spec.js(edit → PUT → texto do header comtestIDdevice-alias-text/device-alias-input/device-alias-save).
| testID | Uso |
|---|---|
device-alias-text |
Texto do alias no header (modo leitura) |
device-alias-input |
Input em modo edição |
device-alias-edit |
Botão para entrar em edição |
device-alias-save |
Botão de confirmar save |
- Redesign da tela device-detail.
- Novas funcionalidades de device além do feedback pós-save do alias.
- Contratos de API além do
savede device já existente. - Configuração de PDV/DISPLAY/PRINT (ver Smoke helpers — device-configuracao).
- Issue: app-community#382
- Wiki do app: Home · ponte ui-common
- Smoke de listagem/navegação relacionada:
ui-common/src/tests/browser/manager/devices-current.spec.js - Code quality / limite ≤ 500 linhas: agents-mcp code-quality