@controleonline/ui-common 1.2.71 → 1.2.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/.github/actions/workers/manager/action.yml +147 -0
  2. package/.github/actions/workers/qa/action.yml +44 -0
  3. package/.github/actions/workers/security/action.yml +44 -0
  4. package/.github/actions/workers/technical-documenter/action.yml +51 -0
  5. package/.github/workflows/manager-worker.yml +168 -0
  6. package/.github/workflows/publish-npm.yml +45 -0
  7. package/.gitmodules +3 -0
  8. package/AGENTS.md +6 -0
  9. package/README.md +14 -0
  10. package/package.json +3 -2
  11. package/src/api/fetch.js +1 -1
  12. package/src/api/index.js +92 -22
  13. package/src/react/components/AddImportModal.js +94 -110
  14. package/src/react/components/BottomNavigationBar.config.js +211 -231
  15. package/src/react/components/CategoryForm.js +52 -35
  16. package/src/react/components/DefaultProvider.native.js +131 -95
  17. package/src/react/components/DefaultProvider.web.js +148 -74
  18. package/src/react/components/NetworkPrinterPreviewModal.js +249 -0
  19. package/src/react/components/NetworkPrinterPreviewModal.styles.js +159 -0
  20. package/src/react/components/RuntimeInfoFooter.js +164 -104
  21. package/src/react/components/RuntimeInfoFooter.styles.js +3 -0
  22. package/src/react/components/UnifiedPaymentBar.js +7 -4
  23. package/src/react/components/UserAvatar.js +160 -0
  24. package/src/react/components/integrations/IntegrationHero/index.js +48 -0
  25. package/src/react/components/integrations/IntegrationHero/styles.js +57 -0
  26. package/src/react/components/integrations/IntegrationTabs/index.js +89 -0
  27. package/src/react/components/integrations/IntegrationTabs/styles.js +44 -0
  28. package/src/react/config/deviceConfigBootstrap.js +5 -3
  29. package/src/react/pages/Connections.js +229 -0
  30. package/src/react/pages/Connections.styles.js +222 -0
  31. package/src/react/pages/DeviceDetailPage.js +2865 -0
  32. package/src/react/pages/DeviceDetailPage.styles.js +308 -0
  33. package/src/react/pages/Devices/currentDevice.js +194 -0
  34. package/src/react/pages/Devices/deviceTypes/DeviceGroupCard.js +257 -0
  35. package/src/react/pages/Devices/deviceTypes/all.js +23 -0
  36. package/src/react/pages/Devices/deviceTypes/device.js +25 -0
  37. package/src/react/pages/Devices/deviceTypes/deviceListHelpers.js +178 -0
  38. package/src/react/pages/Devices/deviceTypes/display.js +26 -0
  39. package/src/react/pages/Devices/deviceTypes/index.js +145 -0
  40. package/src/react/pages/Devices/deviceTypes/ipCamera.js +35 -0
  41. package/src/react/pages/Devices/deviceTypes/pdv.js +88 -0
  42. package/src/react/pages/Devices/deviceTypes/printer.js +37 -0
  43. package/src/react/pages/Devices/deviceTypes/shared.js +453 -0
  44. package/src/react/pages/Devices/deviceTypes/useDeviceNetworkConnectivity.js +145 -0
  45. package/src/react/pages/Devices.js +183 -0
  46. package/src/react/pages/Devices.styles.js +353 -0
  47. package/src/react/pages/Food99IntegrationPage/components/Food99CatalogTab.js +219 -0
  48. package/src/react/pages/Food99IntegrationPage/components/Food99OverviewTab.js +134 -0
  49. package/src/react/pages/Food99IntegrationPage/components/Food99PreviewModal.js +173 -0
  50. package/src/react/pages/Food99IntegrationPage/components/Food99ProductCard.js +1 -0
  51. package/src/react/pages/Food99IntegrationPage/components/Food99QuickWalletModal.js +181 -0
  52. package/src/react/pages/Food99IntegrationPage/components/Food99SettingsTab.js +306 -0
  53. package/src/react/pages/Food99IntegrationPage/components/Food99StoreTab.js +417 -0
  54. package/src/react/pages/Food99IntegrationPage/index.js +1530 -0
  55. package/src/react/pages/Food99IntegrationPage/styles.js +792 -0
  56. package/src/react/pages/Food99IntegrationPage/utils.js +155 -0
  57. package/src/react/pages/Food99IntegrationPage.js +1 -0
  58. package/src/react/pages/Food99IntegrationPage.styles.js +1 -0
  59. package/src/react/pages/GenericLogPage.js +42 -167
  60. package/src/react/pages/IFoodIntegrationPage/components/IFoodCatalogTab.js +8 -0
  61. package/src/react/pages/IFoodIntegrationPage/components/IFoodOperationsTab.js +420 -0
  62. package/src/react/pages/IFoodIntegrationPage/components/IFoodOverviewTab.js +118 -0
  63. package/src/react/pages/IFoodIntegrationPage/components/IFoodPreviewModal.js +103 -0
  64. package/src/react/pages/IFoodIntegrationPage/components/IFoodProductCard.js +1 -0
  65. package/src/react/pages/IFoodIntegrationPage/components/IFoodStoreTab.js +150 -0
  66. package/src/react/pages/IFoodIntegrationPage/index.js +1298 -0
  67. package/src/react/pages/IFoodIntegrationPage/styles.js +764 -0
  68. package/src/react/pages/IFoodIntegrationPage/utils.js +91 -0
  69. package/src/react/pages/IFoodIntegrationPage.js +1 -0
  70. package/src/react/pages/IFoodIntegrationPage.styles.js +1 -0
  71. package/src/react/pages/Imports.js +214 -166
  72. package/src/react/pages/IntegrationConfigPage.js +744 -0
  73. package/src/react/pages/IntegrationConfigPage.styles.js +269 -0
  74. package/src/react/pages/Integrations.js +299 -0
  75. package/src/react/pages/Integrations.styles.js +130 -0
  76. package/src/react/pages/ManagerOrderNotificationsPage.js +642 -0
  77. package/src/react/pages/MarketplaceIntegrationPage/components/MarketplaceProductCard.js +205 -0
  78. package/src/react/pages/MarketplaceIntegrationPage/components/RemoteMarketplaceMenuTab.js +335 -0
  79. package/src/react/pages/MarketplaceIntegrationPage/index.js +60 -0
  80. package/src/react/pages/MarketplaceIntegrationPage/utils.js +137 -0
  81. package/src/react/pages/MercadoLivreIntegrationPage.js +538 -0
  82. package/src/react/pages/PrinterDeviceDetailPage.js +1916 -0
  83. package/src/react/pages/PrinterDeviceDetailPage.styles.js +223 -0
  84. package/src/react/pages/PrinterDeviceFormPage.js +521 -0
  85. package/src/react/pages/PrinterDeviceFormPage.styles.js +159 -0
  86. package/src/react/pages/WhatsAppConnectionPage.js +459 -0
  87. package/src/react/pages/WhatsAppConnectionPage.styles.js +284 -0
  88. package/src/react/pages/integrationsCatalog.js +399 -0
  89. package/src/react/services/Cielo/Cielo.js +193 -193
  90. package/src/react/utils/categoryContexts.js +46 -0
  91. package/src/react/utils/entityDisplay.js +222 -217
  92. package/src/react/utils/fileUrl.js +39 -20
  93. package/src/react/utils/integrationPage.js +16 -0
  94. package/src/react/utils/runtimeFooter.js +40 -22
  95. package/src/react/utils/runtimeLanguage.js +50 -9
  96. package/src/react/utils/runtimeMenu.js +191 -131
  97. package/src/react/utils/runtimeZoom.js +43 -0
  98. package/src/react/utils/shopConfig.js +27 -15
  99. package/src/react/utils/shopFranchises.js +126 -1
  100. package/src/react/utils/storeColumns.js +23 -4
  101. package/src/react/utils/userAvatar.js +104 -0
  102. package/src/store/categories/index.js +48 -20
  103. package/src/store/device_config/index.js +62 -21
  104. package/src/store/entity_log/index.js +22 -1
  105. package/src/store/imports/customActions.js +51 -0
  106. package/src/store/imports/index.js +89 -88
  107. package/src/store/status/index.js +11 -11
  108. package/src/store/themes/index.js +22 -0
  109. package/src/store/timezones/index.js +3 -1
  110. package/src/tests/browser/manager/connections.spec.js +143 -0
  111. package/src/tests/browser/manager/devices-current.spec.js +343 -0
  112. package/src/tests/browser/manager/mercado-livre-integration.spec.js +268 -0
  113. package/src/tests/react/api/deviceHeaders.test.js +114 -0
  114. package/src/tests/react/components/AddImportModal.test.js +166 -0
  115. package/src/tests/react/components/BottomNavigationBar.config.test.js +20 -0
  116. package/src/tests/react/components/UserAvatar.test.js +52 -0
  117. package/src/tests/react/pages/DevicePickerStyles.test.js +47 -0
  118. package/src/tests/react/pages/Devices/deviceListHelpers.test.js +39 -0
  119. package/src/tests/react/pages/DevicesCurrentDevice.test.js +198 -0
  120. package/src/tests/react/pages/connectionsHelpers.test.js +34 -0
  121. package/src/tests/react/pages/mercadoLivreIntegrationPageContract.test.mjs +16 -0
  122. package/src/tests/react/services/Cielo.test.js +190 -190
  123. package/src/tests/react/store/categoriesOrdering.test.js +26 -0
  124. package/src/tests/react/utils/appDomain.test.js +62 -0
  125. package/src/tests/react/utils/entityDisplay.test.js +2 -2
  126. package/src/tests/react/utils/fileUrl.test.js +3 -3
  127. package/src/tests/react/utils/formatter.test.mjs +33 -0
  128. package/src/tests/react/utils/runtimeFooter.test.js +29 -4
  129. package/src/tests/react/utils/runtimeLanguage.test.js +79 -9
  130. package/src/tests/react/utils/runtimeMenu.test.js +198 -134
  131. package/src/tests/react/utils/runtimeZoom.test.js +38 -0
  132. package/src/tests/react/utils/shopConfig.test.js +10 -9
  133. package/src/tests/react/utils/shopFranchises.test.js +43 -0
  134. package/src/tests/react/utils/translate.discovery.test.mjs +170 -0
  135. package/src/tests/react/utils/translate.test.helpers.mjs +152 -0
  136. package/src/tests/react/utils/translate.test.mjs +141 -349
  137. package/src/tests/react/utils/userAvatar.test.js +63 -0
  138. package/src/utils/appDomain.js +15 -4
  139. package/src/utils/formatter.js +104 -16
  140. package/src/utils/integrationConfigs.js +80 -80
  141. package/src/utils/translate.js +222 -438
  142. package/src/utils/translateDiscovery.js +96 -0
  143. package/src/utils/translateNormalize.js +33 -0
  144. package/src/utils/translatePending.js +92 -0
  145. package/src/utils/translateStorage.js +142 -0
  146. package/.github/agents/developer.agent.md +0 -30
  147. package/.github/agents/devops.agent.md +0 -30
  148. package/.github/agents/qa.agent.md +0 -30
  149. package/.github/agents/security.agent.md +0 -30
  150. package/.github/workflows/npmjs.yml +0 -35
@@ -1,11 +1,13 @@
1
1
  const assert = require('node:assert/strict')
2
- const {test} = global
2
+ const {test} = require('node:test')
3
3
 
4
- const {
5
- normalizeLanguageCode,
6
- resolveCompanyLanguageCode,
7
- resolveConfiguredLanguage,
8
- } = require('../../../react/utils/runtimeLanguage')
4
+ const {
5
+ buildTranslationBootstrapKey,
6
+ isTranslationBootstrapReady,
7
+ normalizeLanguageCode,
8
+ resolveCompanyLanguageCode,
9
+ resolveConfiguredLanguage,
10
+ } = require('../../../react/utils/runtimeLanguage')
9
11
 
10
12
  test('normalizes language separators and casing', () => {
11
13
  assert.equal(normalizeLanguageCode(' PT_BR '), 'pt-br')
@@ -46,7 +48,7 @@ test('prefers the selected company language over cached config and session value
46
48
  )
47
49
  })
48
50
 
49
- test('falls back through default company, config, session and the hardcoded default', () => {
51
+ test('falls back through default company, config, session and the hardcoded default', () => {
50
52
  assert.equal(
51
53
  resolveConfiguredLanguage({
52
54
  defaultCompany: {
@@ -76,5 +78,73 @@ test('falls back through default company, config, session and the hardcoded defa
76
78
  'nl-nl',
77
79
  )
78
80
 
79
- assert.equal(resolveConfiguredLanguage({}), 'pt-br')
80
- })
81
+ assert.equal(resolveConfiguredLanguage({}), 'pt-br')
82
+ })
83
+
84
+ test('builds the translation bootstrap key only from language and company context', () => {
85
+ assert.equal(
86
+ buildTranslationBootstrapKey({
87
+ language: ' PT_BR ',
88
+ currentCompanyId: 21,
89
+ defaultCompanyId: 1,
90
+ }),
91
+ 'pt-br::21::1',
92
+ )
93
+ assert.equal(
94
+ buildTranslationBootstrapKey({
95
+ language: 'pt-br',
96
+ currentCompanyId: '',
97
+ defaultCompanyId: 1,
98
+ }),
99
+ '',
100
+ )
101
+ })
102
+
103
+ test('keeps authenticated content blocked until the matching translator exists', () => {
104
+ const context = {
105
+ activeKey: 'pt-br::21::1',
106
+ expectedKey: 'pt-br::21::1',
107
+ required: true,
108
+ ready: true,
109
+ }
110
+
111
+ assert.equal(
112
+ isTranslationBootstrapReady({
113
+ ...context,
114
+ translator: undefined,
115
+ }),
116
+ false,
117
+ )
118
+ assert.equal(
119
+ isTranslationBootstrapReady({
120
+ ...context,
121
+ activeKey: 'pt-br::3::1',
122
+ translator: {t() {}},
123
+ }),
124
+ false,
125
+ )
126
+ assert.equal(
127
+ isTranslationBootstrapReady({
128
+ ...context,
129
+ ready: false,
130
+ translator: {t() {}},
131
+ }),
132
+ false,
133
+ )
134
+ assert.equal(
135
+ isTranslationBootstrapReady({
136
+ ...context,
137
+ translator: {t() {}},
138
+ }),
139
+ true,
140
+ )
141
+ })
142
+
143
+ test('does not block contexts where translation bootstrap is not required', () => {
144
+ assert.equal(
145
+ isTranslationBootstrapReady({
146
+ required: false,
147
+ }),
148
+ true,
149
+ )
150
+ })
@@ -1,38 +1,78 @@
1
1
  /* global describe, expect, it */
2
2
 
3
- import {
4
- filterRuntimeMenuModulesByType,
5
- flattenRuntimeMenuItemsByType,
6
- getRuntimeMenuRoutes,
7
- normalizeRuntimeMenuIcon,
8
- normalizeRuntimeMenuResponse,
9
- userHasRole,
10
- } from '../../../react/utils/runtimeMenu';
3
+ import {
4
+ filterRuntimeMenuModulesByType,
5
+ flattenRuntimeMenuItemsByType,
6
+ getRuntimeMenuRoutes,
7
+ normalizeRuntimeMenuIcon,
8
+ normalizeRuntimeMenuResponse,
9
+ userHasRole,
10
+ } from '../../../react/utils/runtimeMenu';
11
11
 
12
12
  describe('runtimeMenu', () => {
13
13
  it('normalizes legacy menu response modules into sorted arrays', () => {
14
- const result = normalizeRuntimeMenuResponse({
15
- response: {
16
- data: {
17
- modules: {
18
- 2: {
19
- id: 2,
20
- label: 'Config',
21
- menus: [
22
- {id: 20, label: 'B', route: 'BRoute', sortOrder: 20, menuType: 'toolbar'},
23
- {id: 10, label: 'A', route: 'ARoute', sortOrder: 10},
24
- ],
25
- },
26
- },
27
- },
28
- },
29
- });
30
-
31
- expect(result).toHaveLength(1);
32
- expect(result[0].menus.map(item => item.route)).toEqual(['ARoute', 'BRoute']);
33
- expect(result[0].menus[0].routeParams).toEqual({});
34
- expect(result[0].menus.map(item => item.menuType)).toEqual(['home', 'toolbar']);
35
- });
14
+ const result = normalizeRuntimeMenuResponse({
15
+ response: {
16
+ data: {
17
+ modules: {
18
+ 2: {
19
+ id: 2,
20
+ label: 'Config',
21
+ menus: [
22
+ {id: 20, label: 'B', route: 'BRoute', sortOrder: 20, menuType: 'toolbar'},
23
+ {id: 10, label: 'A', route: 'ARoute', sortOrder: 10},
24
+ ],
25
+ },
26
+ },
27
+ },
28
+ },
29
+ });
30
+
31
+ expect(result).toHaveLength(1);
32
+ expect(result[0].menus.map(item => item.route)).toEqual(['ARoute', 'BRoute']);
33
+ expect(result[0].menus[0].routeParams).toEqual({});
34
+ expect(result[0].menus.map(item => item.menuType)).toEqual(['home', 'toolbar']);
35
+ });
36
+
37
+ it('orders runtime modules by category sort order and label', () => {
38
+ const result = normalizeRuntimeMenuResponse({
39
+ modules: {
40
+ operations: {
41
+ label: 'Operacoes',
42
+ sortOrder: 20,
43
+ menus: [
44
+ {label: 'Pedidos', route: 'OrdersRoute', sortOrder: 5},
45
+ ],
46
+ },
47
+ finance: {
48
+ label: 'Financeiro',
49
+ sortOrder: 10,
50
+ menus: [
51
+ {label: 'Financeiro', route: 'FinanceRoute', sortOrder: 60},
52
+ ],
53
+ },
54
+ alpha: {
55
+ label: 'Abertura',
56
+ menus: [
57
+ {label: 'Z menu sem ordenar grupo', route: 'AlphaRoute', sortOrder: 99},
58
+ ],
59
+ },
60
+ config: {
61
+ label: 'Configuracoes',
62
+ menus: [
63
+ {label: 'Config', route: 'ConfigRoute', sortOrder: 1},
64
+ ],
65
+ },
66
+ },
67
+ });
68
+
69
+ expect(result.map(module => module.label)).toEqual([
70
+ 'Abertura',
71
+ 'Configuracoes',
72
+ 'Financeiro',
73
+ 'Operacoes',
74
+ ]);
75
+ });
36
76
 
37
77
  it('builds route sets and checks super admin role', () => {
38
78
  const menus = normalizeRuntimeMenuResponse({
@@ -65,7 +105,7 @@ describe('runtimeMenu', () => {
65
105
  expect(menus[0].menus[0].menuKey).toBe('menu_access');
66
106
  });
67
107
 
68
- it('converts legacy material icon names into feather-compatible names', () => {
108
+ it('converts legacy material icon names into feather-compatible names', () => {
69
109
  expect(normalizeRuntimeMenuIcon('shopping_cart')).toBe('shopping-cart');
70
110
  expect(normalizeRuntimeMenuIcon('account_balance')).toBe('dollar-sign');
71
111
 
@@ -83,112 +123,136 @@ describe('runtimeMenu', () => {
83
123
  },
84
124
  });
85
125
 
86
- expect(menus[0].icon).toBe('dollar-sign');
87
- expect(menus[0].menus[0].icon).toBe('shopping-cart');
88
- });
89
-
90
- it('filters runtime menu modules by menu type', () => {
91
- const menus = normalizeRuntimeMenuResponse({
92
- modules: {
93
- 1: {
94
- label: 'Mix',
95
- menus: [
96
- {route: 'HomePage', menuType: 'home', sortOrder: 10},
97
- {route: 'ToolbarPage', menu_type: 'toolbar', sortOrder: 20},
98
- ],
99
- },
100
- },
101
- });
102
-
103
- const toolbarMenus = filterRuntimeMenuModulesByType(menus, 'toolbar');
104
-
105
- expect(toolbarMenus).toHaveLength(1);
106
- expect(toolbarMenus[0].menus).toHaveLength(1);
107
- expect(toolbarMenus[0].menus[0]).toMatchObject({
108
- route: 'ToolbarPage',
109
- menuType: 'toolbar',
110
- });
111
- expect(
112
- flattenRuntimeMenuItemsByType(menus, 'toolbar').map(item => item.route),
113
- ).toEqual(['ToolbarPage']);
114
- });
115
-
116
- it('falls back to the manager core menu when the runtime menu is empty', () => {
117
- const menus = normalizeRuntimeMenuResponse({}, {appType: 'MANAGER'});
118
-
119
- expect(menus).toHaveLength(4);
126
+ expect(menus[0].icon).toBe('dollar-sign');
127
+ expect(menus[0].menus[0].icon).toBe('shopping-cart');
128
+ });
129
+
130
+ it('filters runtime menu modules by menu type', () => {
131
+ const menus = normalizeRuntimeMenuResponse({
132
+ modules: {
133
+ 1: {
134
+ label: 'Mix',
135
+ menus: [
136
+ {route: 'HomePage', menuType: 'home', sortOrder: 10},
137
+ {route: 'ToolbarPage', menu_type: 'toolbar', sortOrder: 20},
138
+ ],
139
+ },
140
+ },
141
+ });
142
+
143
+ const toolbarMenus = filterRuntimeMenuModulesByType(menus, 'toolbar');
144
+
145
+ expect(toolbarMenus).toHaveLength(1);
146
+ expect(toolbarMenus[0].menus).toHaveLength(1);
147
+ expect(toolbarMenus[0].menus[0]).toMatchObject({
148
+ route: 'ToolbarPage',
149
+ menuType: 'toolbar',
150
+ });
151
+ expect(
152
+ flattenRuntimeMenuItemsByType(menus, 'toolbar').map(item => item.route),
153
+ ).toEqual(['ToolbarPage']);
154
+ });
155
+
156
+ it('falls back to the manager core menu when the runtime menu is empty', () => {
157
+ const menus = normalizeRuntimeMenuResponse({}, {appType: 'MANAGER'});
158
+
159
+ expect(menus).toHaveLength(4);
120
160
  expect(menus.map(module => module.label)).toEqual([
121
161
  'Financeiro',
122
162
  'Operacoes',
123
163
  'Comercial',
124
164
  'Configuracoes',
125
165
  ]);
126
- expect(menus[0].menus[0]).toMatchObject({
127
- label: 'Financeiro',
128
- menuKey: 'financial_hub',
129
- route: 'FinancialHubPage',
130
- menuType: 'home',
131
- });
132
- expect(menus[0].menus[1]).toMatchObject({
133
- label: 'Demonstrativo de resultados',
134
- menuKey: 'income_statement',
135
- route: 'IncomeStatement',
136
- });
137
- expect(menus[1].menus[0]).toMatchObject({
138
- label: 'Pedidos',
139
- menuKey: 'orders',
140
- route: 'OrderHistoryPage',
141
- });
142
- expect(menus[2].menus[0]).toMatchObject({
143
- label: 'Cadastro de clientes',
144
- menuKey: 'clients',
145
- route: 'ClientsIndex',
146
- });
147
- expect(menus[2].menus[1]).toMatchObject({
148
- label: 'Fornecedores',
149
- menuKey: 'providers',
150
- route: 'ProvidersIndex',
151
- });
152
- expect(menus[2].menus[2]).toMatchObject({
153
- label: 'Cadastro de franquias',
154
- menuKey: 'franchisees',
155
- route: 'FranchiseesIndex',
156
- });
157
- expect(menus[3].menus.map(item => item.route)).toEqual([
158
- 'DevicesIndex',
159
- 'ConfiguratorPage',
160
- ]);
161
- });
162
-
163
- it('falls back to the admin starter menu when the runtime menu is empty', () => {
164
- const menus = normalizeRuntimeMenuResponse({}, {appType: 'ADMIN'});
165
-
166
- expect(menus).toHaveLength(1);
167
- expect(menus[0].label).toBe('Configuracoes');
168
- expect(menus[0].menus).toHaveLength(2);
169
- expect(menus[0].menus[0]).toMatchObject({
170
- label: 'Menus por perfil',
171
- menuKey: 'menu_access',
172
- route: 'MenuAccessConfigPage',
173
- menuType: 'home',
174
- });
175
- expect(menus[0].menus[1]).toMatchObject({
176
- label: 'Resultados de testes',
177
- menuKey: 'test_results',
178
- route: 'TestsPlaygroundPage',
179
- menuType: 'home',
180
- });
181
- });
182
-
183
- it('does not invent a fallback menu for non-manager apps', () => {
184
- expect(
185
- normalizeRuntimeMenuResponse({}, {appType: 'SHOP'}),
186
- ).toEqual([]);
187
- });
188
-
189
- it('can skip the manager fallback when requested', () => {
190
- expect(
191
- normalizeRuntimeMenuResponse({}, {appType: 'MANAGER', allowFallback: false}),
192
- ).toEqual([]);
193
- });
194
- });
166
+ expect(menus[0].menus[0]).toMatchObject({
167
+ label: 'Financeiro',
168
+ menuKey: 'financial_hub',
169
+ route: 'FinancialHubPage',
170
+ menuType: 'home',
171
+ });
172
+ expect(menus[0].menus[1]).toMatchObject({
173
+ label: 'Demonstrativo de resultados',
174
+ menuKey: 'income_statement',
175
+ route: 'IncomeStatement',
176
+ });
177
+ expect(menus[1].menus[0]).toMatchObject({
178
+ label: 'Pedidos',
179
+ menuKey: 'orders',
180
+ route: 'OrderHistoryPage',
181
+ });
182
+ expect(menus[2].menus[0]).toMatchObject({
183
+ label: 'Produtos',
184
+ menuKey: 'products',
185
+ route: 'ProductsPage',
186
+ });
187
+ expect(menus[2].menus[1]).toMatchObject({
188
+ label: 'Cadastro de clientes',
189
+ menuKey: 'clients',
190
+ route: 'ClientsIndex',
191
+ });
192
+ expect(menus[2].menus[2]).toMatchObject({
193
+ label: 'Fornecedores',
194
+ menuKey: 'providers',
195
+ route: 'ProvidersIndex',
196
+ });
197
+ expect(menus[2].menus[3]).toMatchObject({
198
+ label: 'Cadastro de franquias',
199
+ menuKey: 'franchisees',
200
+ route: 'FranchiseesIndex',
201
+ });
202
+ expect(menus[3].menus.map(item => item.route)).toEqual([
203
+ 'DevicesIndex',
204
+ 'ConfiguratorPage',
205
+ 'ProductShowcasesPage',
206
+ ]);
207
+ });
208
+
209
+ it('falls back to the admin starter menu when the runtime menu is empty', () => {
210
+ const menus = normalizeRuntimeMenuResponse({}, {appType: 'ADMIN'});
211
+
212
+ expect(menus).toHaveLength(1);
213
+ expect(menus[0].label).toBe('Configuracoes');
214
+ expect(menus[0].menus).toHaveLength(5);
215
+ expect(menus[0].menus[0]).toMatchObject({
216
+ label: 'Menus por perfil',
217
+ menuKey: 'menu_access',
218
+ route: 'MenuAccessConfigPage',
219
+ menuType: 'home',
220
+ });
221
+ expect(menus[0].menus[1]).toMatchObject({
222
+ label: 'Resultados de testes',
223
+ menuKey: 'test_results',
224
+ route: 'TestsPlaygroundPage',
225
+ menuType: 'home',
226
+ });
227
+ expect(menus[0].menus[2]).toMatchObject({
228
+ label: 'Tenancies',
229
+ menuKey: 'tenancies',
230
+ route: 'TenanciesPage',
231
+ menuType: 'home',
232
+ });
233
+ expect(menus[0].menus[3]).toMatchObject({
234
+ label: 'Domínios',
235
+ menuKey: 'people_domains',
236
+ route: 'PeopleDomainsPage',
237
+ menuType: 'home',
238
+ });
239
+ expect(menus[0].menus[4]).toMatchObject({
240
+ label: 'Fluxogramas',
241
+ menuKey: 'flowcharts',
242
+ route: 'FlowchartsPage',
243
+ menuType: 'home',
244
+ });
245
+ });
246
+
247
+ it('does not invent a fallback menu for non-manager apps', () => {
248
+ expect(
249
+ normalizeRuntimeMenuResponse({}, {appType: 'SHOP'}),
250
+ ).toEqual([]);
251
+ });
252
+
253
+ it('can skip the manager fallback when requested', () => {
254
+ expect(
255
+ normalizeRuntimeMenuResponse({}, {appType: 'MANAGER', allowFallback: false}),
256
+ ).toEqual([]);
257
+ });
258
+ });
@@ -0,0 +1,38 @@
1
+ const assert = require('node:assert/strict')
2
+ const {test} = require('node:test')
3
+
4
+ const {
5
+ buildRuntimeZoomStyle,
6
+ normalizeRuntimeZoomPercent,
7
+ resolveRuntimeZoomScale,
8
+ } = require('../../../react/utils/runtimeZoom')
9
+
10
+ test('uses percentage values as the global zoom contract', () => {
11
+ assert.equal(normalizeRuntimeZoomPercent(70), 70)
12
+ assert.equal(resolveRuntimeZoomScale(70), 0.7)
13
+ })
14
+
15
+ test('clamps unsafe zoom percentages', () => {
16
+ assert.equal(normalizeRuntimeZoomPercent(10), 50)
17
+ assert.equal(normalizeRuntimeZoomPercent(200), 150)
18
+ })
19
+
20
+ test('uses css zoom on web to preserve layout scroll bounds', () => {
21
+ assert.deepEqual(
22
+ buildRuntimeZoomStyle(70, {isWeb: true}),
23
+ {
24
+ zoom: 0.7,
25
+ },
26
+ )
27
+ })
28
+
29
+ test('builds compensated container dimensions for native scaled content', () => {
30
+ assert.deepEqual(
31
+ buildRuntimeZoomStyle(70),
32
+ {
33
+ height: '142.85714285714286%',
34
+ transform: [{scale: 0.7}],
35
+ width: '142.85714285714286%',
36
+ },
37
+ )
38
+ })
@@ -3,27 +3,28 @@ const {describe, it} = global;
3
3
 
4
4
  const {
5
5
  resolveShopSettings,
6
- SHOP_LOYALTY_STAMP_ICON_URL_CONFIG_KEY,
7
6
  toggleAndSaveBooleanConfig,
8
7
  } = require('../../../react/utils/shopConfig');
9
8
 
10
9
  describe('shopConfig', () => {
11
- it('reads the configured loyalty stamp icon URL from general settings', () => {
10
+ it('does not expose loyalty stamp artwork as a shop config', () => {
12
11
  const result = resolveShopSettings({
13
- [SHOP_LOYALTY_STAMP_ICON_URL_CONFIG_KEY]:
14
- '"https://cdn.example.com/loyalty-stamp.png"',
12
+ 'shop-loyalty-stamp-icon-url':
13
+ '"https://cdn.example.com/legacy-stamp.png"',
15
14
  });
16
15
 
17
16
  assert.equal(
18
- result.loyaltyStampIconUrl,
19
- 'https://cdn.example.com/loyalty-stamp.png',
17
+ Object.prototype.hasOwnProperty.call(result, 'loyaltyStampIconUrl'),
18
+ false,
20
19
  );
21
20
  });
22
21
 
23
- it('keeps the loyalty stamp icon URL empty when it is not configured', () => {
24
- const result = resolveShopSettings({});
22
+ it('normalizes franchise address category ids from the maps config', () => {
23
+ const result = resolveShopSettings({
24
+ 'shop-franchise-address-category-ids': '["/categories/12", "13", 12]',
25
+ });
25
26
 
26
- assert.equal(result.loyaltyStampIconUrl, '');
27
+ assert.deepEqual(result.franchiseAddressCategoryIds, ['12', '13']);
27
28
  });
28
29
 
29
30
  it('persists the next boolean value when a toggle is clicked', async () => {
@@ -6,6 +6,8 @@ jest.mock('@controleonline/ui-common/src/api', () => ({
6
6
 
7
7
  const {api} = require('@controleonline/ui-common/src/api');
8
8
  const {
9
+ extractAddressCategoryIds,
10
+ filterShopFranchiseDirectory,
9
11
  fetchAllShopFranchiseDirectory,
10
12
  fetchShopFranchiseDirectory,
11
13
  } = require('../../../react/utils/shopFranchises');
@@ -146,4 +148,45 @@ describe('shopFranchises', () => {
146
148
  },
147
149
  ]);
148
150
  });
151
+
152
+ it('extracts category ids from address category payload variants', () => {
153
+ expect(
154
+ extractAddressCategoryIds({
155
+ categories: [{id: 10}, '/categories/11'],
156
+ addressCategories: [{category: {id: 12}}],
157
+ }),
158
+ ).toEqual(['10', '11', '12']);
159
+ });
160
+
161
+ it('filters franchise addresses by selected categories and visible companies', () => {
162
+ const directory = filterShopFranchiseDirectory({
163
+ visibleCompanyIds: [21],
164
+ addressCategoryIds: [12],
165
+ directory: [
166
+ {
167
+ id: 21,
168
+ alias: 'Centro',
169
+ shopAddresses: [
170
+ {id: 501, nickname: 'Loja Centro', categories: [{id: 12}]},
171
+ {id: 502, nickname: 'Loja Oculta', categories: [{id: 13}]},
172
+ ],
173
+ },
174
+ {
175
+ id: 22,
176
+ alias: 'Norte',
177
+ shopAddresses: [{id: 601, categories: [{id: 12}]}],
178
+ },
179
+ ],
180
+ });
181
+
182
+ expect(directory).toEqual([
183
+ {
184
+ id: 21,
185
+ alias: 'Centro',
186
+ shopAddresses: [
187
+ {id: 501, nickname: 'Loja Centro', categories: [{id: 12}]},
188
+ ],
189
+ },
190
+ ]);
191
+ });
149
192
  });