@controleonline/ui-common 1.2.70 → 1.2.75

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 (188) 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 +32 -28
  11. package/src/api/fetch.js +1 -1
  12. package/src/api/index.js +328 -58
  13. package/src/react/components/AddImportModal.js +95 -111
  14. package/src/react/components/AnimatedModal.js +171 -0
  15. package/src/react/components/AnimatedModal.styles.js +21 -0
  16. package/src/react/components/AppTypeSwitcher.js +164 -0
  17. package/src/react/components/AppTypeSwitcher.styles.js +109 -0
  18. package/src/react/components/BackgroundRuntimeBridge.js +5 -4
  19. package/src/react/components/BottomNavigationBar.config.js +211 -231
  20. package/src/react/components/BottomNavigationBar.js +86 -31
  21. package/src/react/components/BottomNavigationBar.styles.js +118 -110
  22. package/src/react/components/CategoryForm.js +52 -35
  23. package/src/react/components/DefaultProvider.native.js +194 -156
  24. package/src/react/components/DefaultProvider.web.js +188 -112
  25. package/src/react/components/DeliveryPushBridge.native.js +2 -2
  26. package/src/react/components/DeviceAlertSoundService.js +3 -3
  27. package/src/react/components/KioskModeBridge.js +2 -2
  28. package/src/react/components/LauncherModeBridge.js +2 -2
  29. package/src/react/components/ManagerPushBridge.native.js +2 -2
  30. package/src/react/components/NetworkPrinterPreviewModal.js +249 -0
  31. package/src/react/components/NetworkPrinterPreviewModal.styles.js +159 -0
  32. package/src/react/components/RemoteCheckoutService.js +28 -20
  33. package/src/react/components/RuntimeBottomNavigationBar.js +146 -119
  34. package/src/react/components/RuntimeInfoFooter.js +137 -45
  35. package/src/react/components/RuntimeInfoFooter.styles.js +3 -0
  36. package/src/react/components/StateStore.js +258 -0
  37. package/src/react/components/UnifiedPaymentBar.js +7 -4
  38. package/src/react/components/UserAvatar.js +160 -0
  39. package/src/react/components/WebsocketListener.native.js +11 -11
  40. package/src/react/components/integrations/IntegrationHero/index.js +48 -0
  41. package/src/react/components/integrations/IntegrationHero/styles.js +57 -0
  42. package/src/react/components/integrations/IntegrationTabs/index.js +89 -0
  43. package/src/react/components/integrations/IntegrationTabs/styles.js +44 -0
  44. package/src/react/config/deviceConfigBootstrap.js +71 -30
  45. package/src/react/css/orders.js +72 -0
  46. package/src/react/pages/Connections.js +229 -0
  47. package/src/react/pages/Connections.styles.js +222 -0
  48. package/src/react/pages/DeviceDetailPage.js +2814 -0
  49. package/src/react/pages/DeviceDetailPage.styles.js +308 -0
  50. package/src/react/pages/Devices/currentDevice.js +194 -0
  51. package/src/react/pages/Devices/deviceTypes/DeviceGroupCard.js +257 -0
  52. package/src/react/pages/Devices/deviceTypes/all.js +23 -0
  53. package/src/react/pages/Devices/deviceTypes/device.js +25 -0
  54. package/src/react/pages/Devices/deviceTypes/deviceListHelpers.js +178 -0
  55. package/src/react/pages/Devices/deviceTypes/display.js +26 -0
  56. package/src/react/pages/Devices/deviceTypes/index.js +145 -0
  57. package/src/react/pages/Devices/deviceTypes/ipCamera.js +35 -0
  58. package/src/react/pages/Devices/deviceTypes/pdv.js +88 -0
  59. package/src/react/pages/Devices/deviceTypes/printer.js +37 -0
  60. package/src/react/pages/Devices/deviceTypes/shared.js +453 -0
  61. package/src/react/pages/Devices/deviceTypes/useDeviceNetworkConnectivity.js +145 -0
  62. package/src/react/pages/Devices.js +183 -0
  63. package/src/react/pages/Devices.styles.js +353 -0
  64. package/src/react/pages/Food99IntegrationPage/components/Food99CatalogTab.js +219 -0
  65. package/src/react/pages/Food99IntegrationPage/components/Food99OverviewTab.js +134 -0
  66. package/src/react/pages/Food99IntegrationPage/components/Food99PreviewModal.js +173 -0
  67. package/src/react/pages/Food99IntegrationPage/components/Food99ProductCard.js +1 -0
  68. package/src/react/pages/Food99IntegrationPage/components/Food99QuickWalletModal.js +181 -0
  69. package/src/react/pages/Food99IntegrationPage/components/Food99SettingsTab.js +306 -0
  70. package/src/react/pages/Food99IntegrationPage/components/Food99StoreTab.js +417 -0
  71. package/src/react/pages/Food99IntegrationPage/index.js +1530 -0
  72. package/src/react/pages/Food99IntegrationPage/styles.js +792 -0
  73. package/src/react/pages/Food99IntegrationPage/utils.js +155 -0
  74. package/src/react/pages/Food99IntegrationPage.js +1 -0
  75. package/src/react/pages/Food99IntegrationPage.styles.js +1 -0
  76. package/src/react/pages/GenericLogPage.js +42 -167
  77. package/src/react/pages/IFoodIntegrationPage/components/IFoodCatalogTab.js +8 -0
  78. package/src/react/pages/IFoodIntegrationPage/components/IFoodOperationsTab.js +420 -0
  79. package/src/react/pages/IFoodIntegrationPage/components/IFoodOverviewTab.js +118 -0
  80. package/src/react/pages/IFoodIntegrationPage/components/IFoodPreviewModal.js +103 -0
  81. package/src/react/pages/IFoodIntegrationPage/components/IFoodProductCard.js +1 -0
  82. package/src/react/pages/IFoodIntegrationPage/components/IFoodStoreTab.js +150 -0
  83. package/src/react/pages/IFoodIntegrationPage/index.js +1298 -0
  84. package/src/react/pages/IFoodIntegrationPage/styles.js +764 -0
  85. package/src/react/pages/IFoodIntegrationPage/utils.js +91 -0
  86. package/src/react/pages/IFoodIntegrationPage.js +1 -0
  87. package/src/react/pages/IFoodIntegrationPage.styles.js +1 -0
  88. package/src/react/pages/Imports.js +214 -166
  89. package/src/react/pages/IntegrationConfigPage.js +744 -0
  90. package/src/react/pages/IntegrationConfigPage.styles.js +269 -0
  91. package/src/react/pages/Integrations.js +299 -0
  92. package/src/react/pages/Integrations.styles.js +130 -0
  93. package/src/react/pages/ManagerOrderNotificationsPage.js +642 -0
  94. package/src/react/pages/MarketplaceIntegrationPage/components/MarketplaceProductCard.js +205 -0
  95. package/src/react/pages/MarketplaceIntegrationPage/components/RemoteMarketplaceMenuTab.js +335 -0
  96. package/src/react/pages/MarketplaceIntegrationPage/index.js +60 -0
  97. package/src/react/pages/MarketplaceIntegrationPage/utils.js +137 -0
  98. package/src/react/pages/MercadoLivreIntegrationPage.js +538 -0
  99. package/src/react/pages/PrinterDeviceDetailPage.js +1916 -0
  100. package/src/react/pages/PrinterDeviceDetailPage.styles.js +223 -0
  101. package/src/react/pages/PrinterDeviceFormPage.js +521 -0
  102. package/src/react/pages/PrinterDeviceFormPage.styles.js +159 -0
  103. package/src/react/pages/SettingsPage/PaymentTypesByWalletTab.js +484 -454
  104. package/src/react/pages/SettingsPage/index.js +1266 -1167
  105. package/src/react/pages/WhatsAppConnectionPage.js +459 -0
  106. package/src/react/pages/WhatsAppConnectionPage.styles.js +284 -0
  107. package/src/react/pages/integrationsCatalog.js +399 -0
  108. package/src/react/print/providers/local.js +1 -1
  109. package/src/react/router/publicRoutes.js +25 -0
  110. package/src/react/services/Cielo/Checkout.js +39 -0
  111. package/src/react/services/Cielo/Cielo.js +193 -0
  112. package/src/react/services/Cielo/Print.js +7 -0
  113. package/src/react/services/InfinitePay/Checkout.js +33 -0
  114. package/src/react/services/InfinitePay/InfinitePay.js +24 -0
  115. package/src/react/{utils → services}/paymentGatewayExecution.js +91 -91
  116. package/src/react/styles/global.js +115 -0
  117. package/src/react/utils/categoryContexts.js +46 -0
  118. package/src/react/utils/entityDisplay.js +222 -217
  119. package/src/react/utils/fileUrl.js +201 -16
  120. package/src/react/utils/integrationPage.js +16 -0
  121. package/src/react/utils/menuNavigation.js +31 -0
  122. package/src/react/utils/orderIdentity.js +277 -0
  123. package/src/react/utils/remotePayment.js +115 -61
  124. package/src/react/utils/runtimeFooter.js +40 -22
  125. package/src/react/utils/runtimeLanguage.js +50 -9
  126. package/src/react/utils/runtimeMenu.js +190 -98
  127. package/src/react/utils/runtimeZoom.js +43 -0
  128. package/src/react/utils/shopConfig.js +72 -34
  129. package/src/react/utils/shopFranchises.js +182 -23
  130. package/src/react/utils/socketRuntimePipeline.js +14 -14
  131. package/src/react/utils/storeColumns.js +23 -4
  132. package/src/react/utils/userAvatar.js +104 -0
  133. package/src/store/categories/index.js +48 -20
  134. package/src/store/configs/index.js +2 -2
  135. package/src/store/device_config/index.js +62 -21
  136. package/src/store/entity_log/index.js +22 -1
  137. package/src/store/imports/customActions.js +51 -0
  138. package/src/store/imports/index.js +89 -88
  139. package/src/store/status/index.js +11 -11
  140. package/src/store/themes/index.js +22 -0
  141. package/src/store/timezones/index.js +3 -1
  142. package/src/tests/browser/manager/connections.spec.js +143 -0
  143. package/src/tests/browser/manager/devices-current.spec.js +343 -0
  144. package/src/tests/browser/manager/mercado-livre-integration.spec.js +268 -0
  145. package/src/tests/react/api/deviceHeaders.test.js +114 -0
  146. package/src/tests/react/api/loadSmokeIndex.test.js +75 -0
  147. package/src/tests/react/components/AddImportModal.test.js +166 -0
  148. package/src/tests/react/components/BottomNavigationBar.config.test.js +20 -0
  149. package/src/tests/react/components/UserAvatar.test.js +52 -0
  150. package/src/tests/react/config/deviceConfigBootstrap.test.js +47 -0
  151. package/src/tests/react/pages/DevicePickerStyles.test.js +47 -0
  152. package/src/tests/react/pages/Devices/deviceListHelpers.test.js +39 -0
  153. package/src/tests/react/pages/DevicesCurrentDevice.test.js +198 -0
  154. package/src/tests/react/pages/connectionsHelpers.test.js +34 -0
  155. package/src/tests/react/pages/mercadoLivreIntegrationPageContract.test.mjs +16 -0
  156. package/src/tests/react/print/localPrintProvider.test.js +1 -1
  157. package/src/tests/react/services/Cielo.test.js +190 -0
  158. package/src/tests/react/store/categoriesOrdering.test.js +26 -0
  159. package/src/tests/react/utils/appDomain.test.js +62 -0
  160. package/src/tests/react/utils/entityDisplay.test.js +2 -2
  161. package/src/tests/react/utils/fileUrl.test.js +62 -0
  162. package/src/tests/react/utils/formatter.test.mjs +33 -0
  163. package/src/tests/react/utils/importStatus.test.js +2 -2
  164. package/src/tests/react/utils/orderIdentity.test.js +139 -0
  165. package/src/tests/react/utils/remotePayment.test.js +62 -0
  166. package/src/tests/react/utils/runtimeFooter.test.js +32 -9
  167. package/src/tests/react/utils/runtimeLanguage.test.js +80 -10
  168. package/src/tests/react/utils/runtimeMenu.test.js +195 -111
  169. package/src/tests/react/utils/runtimeZoom.test.js +38 -0
  170. package/src/tests/react/utils/shopConfig.test.js +75 -0
  171. package/src/tests/react/utils/shopFranchises.test.js +122 -12
  172. package/src/tests/react/utils/translate.discovery.test.mjs +170 -0
  173. package/src/tests/react/utils/translate.test.helpers.mjs +152 -0
  174. package/src/tests/react/utils/translate.test.mjs +252 -396
  175. package/src/tests/react/utils/userAvatar.test.js +63 -0
  176. package/src/utils/appDomain.js +15 -4
  177. package/src/utils/formatter.js +122 -16
  178. package/src/utils/integrationConfigs.js +1 -0
  179. package/src/utils/translate.js +277 -398
  180. package/src/utils/translateDiscovery.js +96 -0
  181. package/src/utils/translateNormalize.js +33 -0
  182. package/src/utils/translatePending.js +92 -0
  183. package/src/utils/translateStorage.js +142 -0
  184. package/.github/agents/developer.agent.md +0 -30
  185. package/.github/agents/devops.agent.md +0 -30
  186. package/.github/agents/qa.agent.md +0 -30
  187. package/.github/agents/security.agent.md +0 -30
  188. package/.github/workflows/npmjs.yml +0 -35
@@ -0,0 +1,43 @@
1
+ export const DEFAULT_RUNTIME_ZOOM_PERCENT = 100;
2
+ export const MIN_RUNTIME_ZOOM_PERCENT = 50;
3
+ export const MAX_RUNTIME_ZOOM_PERCENT = 150;
4
+
5
+ export const normalizeRuntimeZoomPercent = value => {
6
+ if (value === undefined || value === null || value === '') {
7
+ return DEFAULT_RUNTIME_ZOOM_PERCENT;
8
+ }
9
+
10
+ const numericValue = Number(String(value).replace('%', '').trim());
11
+
12
+ if (!Number.isFinite(numericValue)) {
13
+ return DEFAULT_RUNTIME_ZOOM_PERCENT;
14
+ }
15
+
16
+ return Math.min(
17
+ MAX_RUNTIME_ZOOM_PERCENT,
18
+ Math.max(MIN_RUNTIME_ZOOM_PERCENT, numericValue),
19
+ );
20
+ };
21
+
22
+ export const resolveRuntimeZoomScale = value =>
23
+ normalizeRuntimeZoomPercent(value) / 100;
24
+
25
+ export const buildRuntimeZoomStyle = (value, {isWeb = false} = {}) => {
26
+ const scale = resolveRuntimeZoomScale(value);
27
+
28
+ if (scale === 1) {
29
+ return null;
30
+ }
31
+
32
+ if (isWeb) {
33
+ return {zoom: scale};
34
+ }
35
+
36
+ const inverseSize = `${100 / scale}%`;
37
+
38
+ return {
39
+ height: inverseSize,
40
+ transform: [{scale}],
41
+ width: inverseSize,
42
+ };
43
+ };
@@ -38,20 +38,29 @@ export const SHOP_CATALOG_PRODUCT_TYPES_CONFIG_KEY =
38
38
  'shop-catalog-product-types';
39
39
  export const SHOP_GOOGLE_MAPS_API_KEY_CONFIG_KEY =
40
40
  'shop-google-maps-api-key';
41
- export const SHOP_FRANCHISE_PIN_ICON_URL_CONFIG_KEY =
42
- 'shop-franchise-pin-icon-url';
43
41
  export const SHOP_FRANCHISE_VISIBLE_COMPANY_IDS_CONFIG_KEY =
44
42
  'shop-franchise-visible-company-ids';
45
- export const SHOP_FRANCHISE_VISIBLE_ADDRESS_IDS_CONFIG_KEY =
46
- 'shop-franchise-visible-address-ids';
43
+ export const SHOP_FRANCHISE_VISIBLE_ADDRESS_IDS_CONFIG_KEY =
44
+ 'shop-franchise-visible-address-ids';
45
+ export const SHOP_FRANCHISE_ADDRESS_CATEGORY_IDS_CONFIG_KEY =
46
+ 'shop-franchise-address-category-ids';
47
+ export const SHOP_FRANCHISE_ADDRESS_CATEGORY_CONTEXT =
48
+ 'shop-franchise-address';
47
49
  export const SHOP_LOYALTY_COUPONS_ENABLED_CONFIG_KEY =
48
50
  'shop-loyalty-coupons-enabled';
49
- export const SHOP_LOYALTY_PRODUCT_IDS_CONFIG_KEY =
50
- 'shop-loyalty-product-ids';
51
- export const SHOP_LOYALTY_REQUIRED_SALES_CONFIG_KEY =
52
- 'shop-loyalty-required-sales';
53
- export const SHOP_LOYALTY_GIFT_PRODUCT_ID_CONFIG_KEY =
54
- 'shop-loyalty-gift-product-id';
51
+ export const SHOP_LOYALTY_PRODUCT_IDS_CONFIG_KEY =
52
+ 'shop-loyalty-product-ids';
53
+ export const SHOP_LOYALTY_REQUIRED_SALES_CONFIG_KEY =
54
+ 'shop-loyalty-required-sales';
55
+ export const SHOP_LOYALTY_GIFT_PRODUCT_ID_CONFIG_KEY =
56
+ 'shop-loyalty-gift-product-id';
57
+ export const SHOP_SHOWCASE_TYPE_SETTING_KEY = 'shop_type';
58
+ export const SHOP_SHOWCASE_TYPE_MENU = 'menu';
59
+ export const SHOP_SHOWCASE_TYPE_ECOMMERCE = 'ecommerce';
60
+ export const SHOP_SHOWCASE_TYPE_OPTIONS = [
61
+ SHOP_SHOWCASE_TYPE_MENU,
62
+ SHOP_SHOWCASE_TYPE_ECOMMERCE,
63
+ ];
55
64
 
56
65
  export const normalizeShopEntityId = value => {
57
66
  if (!value) {
@@ -180,6 +189,14 @@ export const normalizeShopMoneyConfig = (value, fallback = 0) => {
180
189
 
181
190
  return Number.isFinite(normalized) ? Math.max(0, normalized) : fallback;
182
191
  };
192
+
193
+ export const normalizeShopShowcaseType = value => {
194
+ const normalized = String(value || '').trim().toLowerCase();
195
+
196
+ return SHOP_SHOWCASE_TYPE_OPTIONS.includes(normalized)
197
+ ? normalized
198
+ : SHOP_SHOWCASE_TYPE_MENU;
199
+ };
183
200
 
184
201
  export const getEnabledShopHomeOptions = ({
185
202
  salesPageEnabled,
@@ -218,11 +235,11 @@ export const normalizeShopPrimaryEntry = (
218
235
  return enabledOptions[0] || '';
219
236
  };
220
237
 
221
- export const resolveShopSettings = configs => {
222
- const configMap =
223
- configs && typeof configs === 'object' && !Array.isArray(configs)
224
- ? configs
225
- : {};
238
+ export const resolveShopSettings = configs => {
239
+ const configMap =
240
+ configs && typeof configs === 'object' && !Array.isArray(configs)
241
+ ? configs
242
+ : {};
226
243
 
227
244
  const salesPageEnabled = normalizeBooleanConfig(
228
245
  configMap[SHOP_SALES_PAGE_ENABLED_CONFIG_KEY],
@@ -241,11 +258,11 @@ export const resolveShopSettings = configs => {
241
258
  configMap[SHOP_LOYALTY_COUPONS_ENABLED_CONFIG_KEY],
242
259
  );
243
260
 
244
- return {
245
- salesPageEnabled,
246
- franchiseLocatorEnabled,
247
- loyaltyCouponsEnabled,
248
- enabledHomeOptions,
261
+ return {
262
+ salesPageEnabled,
263
+ franchiseLocatorEnabled,
264
+ loyaltyCouponsEnabled,
265
+ enabledHomeOptions,
249
266
  primaryEntry: normalizeShopPrimaryEntry(
250
267
  configMap[SHOP_PRIMARY_ENTRY_CONFIG_KEY],
251
268
  {
@@ -272,23 +289,44 @@ export const resolveShopSettings = configs => {
272
289
  googleMapsApiKey: normalizeShopTextConfig(
273
290
  configMap[SHOP_GOOGLE_MAPS_API_KEY_CONFIG_KEY],
274
291
  ),
275
- franchisePinIconUrl: normalizeShopTextConfig(
276
- configMap[SHOP_FRANCHISE_PIN_ICON_URL_CONFIG_KEY],
277
- ),
278
292
  visibleFranchiseCompanyIds: normalizeShopEntityIds(
279
293
  configMap[SHOP_FRANCHISE_VISIBLE_COMPANY_IDS_CONFIG_KEY],
280
294
  ),
281
- visibleFranchiseAddressIds: normalizeShopEntityIds(
282
- configMap[SHOP_FRANCHISE_VISIBLE_ADDRESS_IDS_CONFIG_KEY],
283
- ),
295
+ visibleFranchiseAddressIds: normalizeShopEntityIds(
296
+ configMap[SHOP_FRANCHISE_VISIBLE_ADDRESS_IDS_CONFIG_KEY],
297
+ ),
298
+ franchiseAddressCategoryIds: normalizeShopEntityIds(
299
+ configMap[SHOP_FRANCHISE_ADDRESS_CATEGORY_IDS_CONFIG_KEY],
300
+ ),
284
301
  loyaltyProductIds: normalizeShopEntityIds(
285
302
  configMap[SHOP_LOYALTY_PRODUCT_IDS_CONFIG_KEY],
286
303
  ),
287
- loyaltyRequiredSales: normalizeShopLoyaltyRequiredSales(
288
- configMap[SHOP_LOYALTY_REQUIRED_SALES_CONFIG_KEY],
289
- ),
290
- loyaltyGiftProductId: normalizeShopEntityId(
291
- configMap[SHOP_LOYALTY_GIFT_PRODUCT_ID_CONFIG_KEY],
292
- ),
293
- };
294
- };
304
+ loyaltyRequiredSales: normalizeShopLoyaltyRequiredSales(
305
+ configMap[SHOP_LOYALTY_REQUIRED_SALES_CONFIG_KEY],
306
+ ),
307
+ loyaltyGiftProductId: normalizeShopEntityId(
308
+ configMap[SHOP_LOYALTY_GIFT_PRODUCT_ID_CONFIG_KEY],
309
+ ),
310
+ };
311
+ };
312
+
313
+ export const saveAndUpdateConfigValue = ({
314
+ configKey,
315
+ nextValue,
316
+ saveConfig,
317
+ setValue,
318
+ }) => {
319
+ setValue(nextValue);
320
+ return saveConfig(configKey, nextValue);
321
+ };
322
+
323
+ export const toggleAndSaveBooleanConfig = ({
324
+ configKey,
325
+ currentValue,
326
+ saveConfig,
327
+ setValue,
328
+ }) => {
329
+ const nextValue = !Boolean(currentValue);
330
+ setValue(nextValue);
331
+ return saveConfig(configKey, nextValue ? '1' : '0');
332
+ };
@@ -3,19 +3,153 @@ import {
3
3
  extractCollectionItems,
4
4
  toEntityIri,
5
5
  } from '@controleonline/ui-common/src/react/utils/commercialDocumentOrders';
6
- import {normalizeShopEntityId} from '@controleonline/ui-common/src/react/utils/shopConfig';
6
+ import {normalizeShopEntityId} from '@controleonline/ui-common/src/react/utils/shopConfig';
7
7
 
8
8
  export const SHOP_FRANCHISE_LINK_TYPE = 'franchisee';
9
9
  export const SHOP_FRANCHISE_PAGE_SIZE = 50;
10
10
 
11
11
  const normalizeItemsPerPage = value =>
12
12
  Math.max(1, Math.min(SHOP_FRANCHISE_PAGE_SIZE, Number(value) || SHOP_FRANCHISE_PAGE_SIZE));
13
-
14
- const sortByLabel = (left, right) =>
15
- String(left || '')
16
- .localeCompare(String(right || ''), 'pt-BR', {
17
- sensitivity: 'base',
18
- });
13
+
14
+ const normalizeFranchiseDirectoryItem = company => ({
15
+ ...company,
16
+ shopAddresses: Array.isArray(company?.shopAddresses)
17
+ ? company.shopAddresses
18
+ : Array.isArray(company?.address)
19
+ ? company.address
20
+ : [],
21
+ });
22
+
23
+ const sortByLabel = (left, right) =>
24
+ String(left || '')
25
+ .localeCompare(String(right || ''), 'pt-BR', {
26
+ sensitivity: 'base',
27
+ });
28
+
29
+ const normalizeCategoryCandidate = value => {
30
+ const normalizedId = normalizeShopEntityId(value);
31
+
32
+ if (normalizedId) {
33
+ return normalizedId;
34
+ }
35
+
36
+ if (typeof value === 'string' || typeof value === 'number') {
37
+ return String(value || '').trim();
38
+ }
39
+
40
+ return '';
41
+ };
42
+
43
+ export const extractAddressCategoryIds = address => {
44
+ const candidates = [
45
+ address?.categories,
46
+ address?.category,
47
+ address?.addressCategories,
48
+ address?.address_categories,
49
+ address?.categoryAddresses,
50
+ address?.category_addresses,
51
+ ];
52
+ const result = new Set();
53
+
54
+ const collect = value => {
55
+ if (value === null || value === undefined || value === '') {
56
+ return;
57
+ }
58
+
59
+ if (Array.isArray(value)) {
60
+ value.forEach(collect);
61
+ return;
62
+ }
63
+
64
+ if (typeof value === 'object') {
65
+ [
66
+ value.category,
67
+ value.category_id,
68
+ value.categoryId,
69
+ value.id,
70
+ value['@id'],
71
+ ].forEach(collect);
72
+ return;
73
+ }
74
+
75
+ String(value)
76
+ .split(/\r?\n|,/)
77
+ .map(normalizeCategoryCandidate)
78
+ .filter(Boolean)
79
+ .forEach(id => result.add(id));
80
+ };
81
+
82
+ candidates.forEach(collect);
83
+
84
+ return Array.from(result);
85
+ };
86
+
87
+ export const addressMatchesFranchiseCategoryIds = (
88
+ address,
89
+ categoryIds = [],
90
+ ) => {
91
+ const selectedCategoryIds = new Set(
92
+ (Array.isArray(categoryIds) ? categoryIds : [])
93
+ .map(normalizeCategoryCandidate)
94
+ .filter(Boolean),
95
+ );
96
+
97
+ if (selectedCategoryIds.size === 0) {
98
+ return true;
99
+ }
100
+
101
+ return extractAddressCategoryIds(address).some(categoryId =>
102
+ selectedCategoryIds.has(categoryId),
103
+ );
104
+ };
105
+
106
+ export const filterShopFranchiseDirectory = ({
107
+ directory = [],
108
+ visibleCompanyIds = [],
109
+ addressCategoryIds = [],
110
+ legacyVisibleAddressIds = [],
111
+ } = {}) => {
112
+ const visibleCompanyIdSet = new Set(
113
+ (Array.isArray(visibleCompanyIds) ? visibleCompanyIds : [])
114
+ .map(normalizeShopEntityId)
115
+ .filter(Boolean),
116
+ );
117
+ const legacyVisibleAddressIdSet = new Set(
118
+ (Array.isArray(legacyVisibleAddressIds) ? legacyVisibleAddressIds : [])
119
+ .map(normalizeShopEntityId)
120
+ .filter(Boolean),
121
+ );
122
+
123
+ if (visibleCompanyIdSet.size === 0) {
124
+ return [];
125
+ }
126
+
127
+ return (Array.isArray(directory) ? directory : [])
128
+ .map(company => {
129
+ const companyId = normalizeShopEntityId(company);
130
+
131
+ if (!visibleCompanyIdSet.has(companyId)) {
132
+ return null;
133
+ }
134
+
135
+ const shopAddresses = (company?.shopAddresses || []).filter(address => {
136
+ if (!addressMatchesFranchiseCategoryIds(address, addressCategoryIds)) {
137
+ return false;
138
+ }
139
+
140
+ return (
141
+ legacyVisibleAddressIdSet.size === 0 ||
142
+ legacyVisibleAddressIdSet.has(normalizeShopEntityId(address))
143
+ );
144
+ });
145
+
146
+ return {
147
+ ...company,
148
+ shopAddresses,
149
+ };
150
+ })
151
+ .filter(Boolean);
152
+ };
19
153
 
20
154
  export const resolveFranchiseCompanyLabel = company =>
21
155
  String(company?.alias || company?.name || '').trim() ||
@@ -49,14 +183,46 @@ export const fetchShopFranchiseCompanies = async ({
49
183
  const items = extractCollectionItems(response);
50
184
 
51
185
  return items
52
- .map(company => ({
53
- ...company,
54
- shopAddresses: Array.isArray(company?.shopAddresses)
55
- ? company.shopAddresses
56
- : Array.isArray(company?.address)
57
- ? company.address
58
- : [],
59
- }))
186
+ .map(normalizeFranchiseDirectoryItem)
187
+ .sort((left, right) =>
188
+ sortByLabel(
189
+ resolveFranchiseCompanyLabel(left),
190
+ resolveFranchiseCompanyLabel(right),
191
+ ),
192
+ );
193
+ };
194
+
195
+ export const fetchAllShopFranchiseDirectory = async ({
196
+ companyId,
197
+ publicDirectory = false,
198
+ search = '',
199
+ itemsPerPage = SHOP_FRANCHISE_PAGE_SIZE,
200
+ } = {}) => {
201
+ const normalizedItemsPerPage = normalizeItemsPerPage(itemsPerPage);
202
+ const items = [];
203
+ let page = 1;
204
+
205
+ while (true) {
206
+ const pageItems = await fetchShopFranchiseCompanies({
207
+ companyId,
208
+ publicDirectory,
209
+ search,
210
+ page,
211
+ itemsPerPage: normalizedItemsPerPage,
212
+ });
213
+ const normalizedPageItems = Array.isArray(pageItems) ? pageItems : [];
214
+
215
+ items.push(...normalizedPageItems);
216
+
217
+ if (normalizedPageItems.length < normalizedItemsPerPage) {
218
+ break;
219
+ }
220
+
221
+ page += 1;
222
+ }
223
+
224
+ return items
225
+ .map(normalizeFranchiseDirectoryItem)
60
226
  .sort((left, right) =>
61
227
  sortByLabel(
62
228
  resolveFranchiseCompanyLabel(left),
@@ -102,13 +268,6 @@ export const fetchShopFranchiseDirectory = async ({
102
268
  itemsPerPage,
103
269
  });
104
270
 
105
- return companies.map(company => ({
106
- ...company,
107
- shopAddresses: Array.isArray(company?.shopAddresses)
108
- ? company.shopAddresses
109
- : Array.isArray(company?.address)
110
- ? company.address
111
- : [],
112
- }));
271
+ return companies.map(normalizeFranchiseDirectoryItem);
113
272
  };
114
273
  // TODO(store-first): quando este arquivo for mexido, mover a leitura para stores, remover api.fetch e evitar repassar dados em objetos quando o store ja resolver isso.
@@ -104,15 +104,15 @@ const createSocketRuntimePipeline = ({
104
104
  : 'socket:open'
105
105
  : `socket:${normalizedStatus || 'off'}`;
106
106
 
107
- const indicatorColor =
108
- socketConnected && socketIdentified
109
- ? '#10b981'
110
- : (socketConnected ||
111
- ['connecting', 'identifying', 'open', 'reconnecting'].includes(
112
- normalizedStatus,
113
- ))
114
- ? '#e67e22'
115
- : '#c10015';
107
+ const indicatorTone =
108
+ socketConnected && socketIdentified
109
+ ? 'success'
110
+ : (socketConnected ||
111
+ ['connecting', 'identifying', 'open', 'reconnecting'].includes(
112
+ normalizedStatus,
113
+ ))
114
+ ? 'warning'
115
+ : 'error';
116
116
 
117
117
  const lastSocketStores = Array.isArray(state?.lastStores)
118
118
  ? state.lastStores.filter(Boolean).join(', ')
@@ -122,11 +122,11 @@ const createSocketRuntimePipeline = ({
122
122
  : '';
123
123
 
124
124
  runtimeDebugActions.setFooterEntry({
125
- key: 'socket',
126
- order: 10,
127
- indicatorColor,
128
- updatedAt: state?.updatedAt || new Date().toISOString(),
129
- lines: [
125
+ key: 'socket',
126
+ order: 10,
127
+ indicatorTone,
128
+ updatedAt: state?.updatedAt || new Date().toISOString(),
129
+ lines: [
130
130
  `Realtime ${stateLabel} | empresa: ${normalizeText(
131
131
  getCurrentCompanyId(),
132
132
  ) || '--'} | device: ${truncateText(
@@ -116,10 +116,29 @@ const normalizeFormattedValue = value => {
116
116
  if (value === null || value === undefined) {
117
117
  return value;
118
118
  }
119
-
120
- if (typeof value === 'object' && !Array.isArray(value)) {
121
- return value?.label ?? value?.value ?? value?.name ?? value;
122
- }
119
+
120
+ if (Array.isArray(value)) {
121
+ return value
122
+ .map(item => normalizeFormattedValue(item))
123
+ .filter(item => normalizeText(item))
124
+ .join(', ');
125
+ }
126
+
127
+ if (typeof value === 'object' && !Array.isArray(value)) {
128
+ return (
129
+ value?.label ??
130
+ value?.value ??
131
+ value?.name ??
132
+ value?.alias ??
133
+ value?.icon ??
134
+ value?.fileName ??
135
+ value?.filename ??
136
+ value?.originalName ??
137
+ value?.id ??
138
+ value?.['@id'] ??
139
+ ''
140
+ );
141
+ }
123
142
 
124
143
  return value;
125
144
  };
@@ -0,0 +1,104 @@
1
+ import md5 from 'md5';
2
+
3
+ const normalizeText = value => String(value || '').replace(/\s+/g, ' ').trim();
4
+
5
+ const extractNumericId = value => {
6
+ const normalized = normalizeText(value);
7
+ if (!normalized) {
8
+ return '';
9
+ }
10
+
11
+ if (/^\d+$/.test(normalized)) {
12
+ return normalized;
13
+ }
14
+
15
+ const match = normalized.match(/\/(\d+)(?:\/)?$/);
16
+ return match ? match[1] : '';
17
+ };
18
+
19
+ export const getAvatarDisplayName = user =>
20
+ normalizeText(
21
+ user?.name ||
22
+ user?.people?.name ||
23
+ user?.person?.name ||
24
+ user?.realname ||
25
+ user?.username,
26
+ );
27
+
28
+ export const resolveUserAvatarUrl = (user, resolvePersistedAvatar) => {
29
+ return (
30
+ typeof resolvePersistedAvatar === 'function'
31
+ ? normalizeText(resolvePersistedAvatar(user?.avatar))
32
+ : ''
33
+ );
34
+ };
35
+
36
+ export const resolveUserPeopleIri = (user, session = {}) => {
37
+ const candidates = [
38
+ session?.people,
39
+ session?.person,
40
+ session?.peopleId,
41
+ session?.people_id,
42
+ user?.people?.['@id'],
43
+ user?.people?.id,
44
+ user?.people,
45
+ user?.person?.['@id'],
46
+ user?.person?.id,
47
+ user?.person,
48
+ user?.peopleId,
49
+ user?.people_id,
50
+ user?.person_id,
51
+ user?.['@id'],
52
+ user?.id,
53
+ ];
54
+
55
+ for (const candidate of candidates) {
56
+ const normalized = normalizeText(candidate);
57
+ if (!normalized) {
58
+ continue;
59
+ }
60
+
61
+ if (normalized.includes('/people/')) {
62
+ return normalized;
63
+ }
64
+
65
+ const id = extractNumericId(normalized);
66
+ if (id) {
67
+ return `/people/${id}`;
68
+ }
69
+ }
70
+
71
+ return '';
72
+ };
73
+
74
+ export const getUserInitials = ({name, email} = {}) => {
75
+ const words = normalizeText(name).split(' ').filter(Boolean);
76
+
77
+ if (words.length >= 2) {
78
+ return `${words[0][0]}${words[words.length - 1][0]}`.toUpperCase();
79
+ }
80
+
81
+ if (words.length === 1) {
82
+ return words[0][0].toUpperCase();
83
+ }
84
+
85
+ const normalizedEmail = normalizeText(email);
86
+ return normalizedEmail ? normalizedEmail[0].toUpperCase() : '?';
87
+ };
88
+
89
+ export const getGravatarUrl = (email, size = 200) => {
90
+ const normalizedEmail = normalizeText(email).toLowerCase();
91
+ if (!normalizedEmail) {
92
+ return '';
93
+ }
94
+
95
+ return `https://www.gravatar.com/avatar/${md5(normalizedEmail)}?s=${size}&d=404`;
96
+ };
97
+
98
+ export default {
99
+ getAvatarDisplayName,
100
+ getGravatarUrl,
101
+ getUserInitials,
102
+ resolveUserAvatarUrl,
103
+ resolveUserPeopleIri,
104
+ };
@@ -8,11 +8,20 @@ export default {
8
8
  item: {},
9
9
  items: [],
10
10
  filters: {},
11
- resourceEndpoint: "categories",
12
- isLoading: false,
13
- error: "",
14
-
15
- totalItems: 0,messages:[], message:{},
11
+ resourceEndpoint: "categories",
12
+ isLoading: false,
13
+ error: "",
14
+ configs: {
15
+ import: {
16
+ enabled: true,
17
+ importType: "product",
18
+ labelKey: ["categories", "button", "importCsv"],
19
+ titleKey: ["categories", "title", "productImport"],
20
+ searchPlaceholderKey: ["categories", "input", "importSearch"],
21
+ },
22
+ },
23
+
24
+ totalItems: 0,messages:[], message:{},
16
25
 
17
26
  summary: {},
18
27
  filters: {},
@@ -29,17 +38,34 @@ export default {
29
38
  return "#" + value;
30
39
  },
31
40
  },
32
- {
33
- externalFilter: false,
34
- sortable: true,
35
- name: "name",
41
+ {
42
+ externalFilter: false,
43
+ sortable: true,
44
+ name: "name",
36
45
  align: "left",
37
46
  label: "name",
38
47
  sum: false,
39
48
  format: function (value) {
40
49
  return value;
41
- },
42
- },
50
+ },
51
+ },
52
+ {
53
+ sortable: true,
54
+ editable: true,
55
+ inputType: "number",
56
+ name: "sortOrder",
57
+ align: "right",
58
+ label: "sortOrder",
59
+ defaultSort: "ASC",
60
+ format: function (value) {
61
+ return value ?? "";
62
+ },
63
+ saveFormat: function (value) {
64
+ return value === "" || value === null || value === undefined
65
+ ? null
66
+ : Number(value);
67
+ },
68
+ },
43
69
  {
44
70
  sortable: true,
45
71
  name: "color",
@@ -60,15 +86,17 @@ export default {
60
86
  return value;
61
87
  },
62
88
  },
63
- {
64
- sortable: true,
65
- name: "categoryFiles",
66
- align: "left",
67
- label: "categoryFiles",
68
- format: function (value) {
69
- return value;
70
- },
71
- },
89
+ {
90
+ sortable: true,
91
+ name: "categoryFiles",
92
+ align: "left",
93
+ label: "categoryFiles",
94
+ inputType: "files",
95
+ fileType: "image",
96
+ format: function (value) {
97
+ return value;
98
+ },
99
+ },
72
100
  {
73
101
  externalFilter: false,
74
102
  sortable: true,