@hed-hog/core 0.0.387 → 0.0.393

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 (935) hide show
  1. package/README.md +51 -11
  2. package/dist/access-log/access-log.controller.d.ts.map +1 -1
  3. package/dist/access-log/access-log.controller.js +1 -1
  4. package/dist/access-log/access-log.controller.js.map +1 -1
  5. package/dist/access-log/access-log.interceptor.d.ts.map +1 -1
  6. package/dist/access-log/access-log.interceptor.js +11 -6
  7. package/dist/access-log/access-log.interceptor.js.map +1 -1
  8. package/dist/access-log/access-log.service.d.ts +4 -0
  9. package/dist/access-log/access-log.service.d.ts.map +1 -1
  10. package/dist/access-log/access-log.service.js +31 -4
  11. package/dist/access-log/access-log.service.js.map +1 -1
  12. package/dist/access-log/dto/query-access-log.dto.d.ts +2 -0
  13. package/dist/access-log/dto/query-access-log.dto.d.ts.map +1 -1
  14. package/dist/access-log/dto/query-access-log.dto.js +5 -0
  15. package/dist/access-log/dto/query-access-log.dto.js.map +1 -1
  16. package/dist/ai/ai-attachment.fragments.d.ts +23 -0
  17. package/dist/ai/ai-attachment.fragments.d.ts.map +1 -0
  18. package/dist/ai/ai-attachment.fragments.js +34 -0
  19. package/dist/ai/ai-attachment.fragments.js.map +1 -0
  20. package/dist/ai/ai.controller.d.ts +12 -17
  21. package/dist/ai/ai.controller.d.ts.map +1 -1
  22. package/dist/ai/ai.controller.js +17 -2
  23. package/dist/ai/ai.controller.js.map +1 -1
  24. package/dist/ai/ai.module.d.ts.map +1 -1
  25. package/dist/ai/ai.module.js +3 -2
  26. package/dist/ai/ai.module.js.map +1 -1
  27. package/dist/ai/ai.prompts.d.ts +16 -0
  28. package/dist/ai/ai.prompts.d.ts.map +1 -0
  29. package/dist/ai/ai.prompts.js +37 -0
  30. package/dist/ai/ai.prompts.js.map +1 -0
  31. package/dist/ai/ai.service.d.ts +92 -12
  32. package/dist/ai/ai.service.d.ts.map +1 -1
  33. package/dist/ai/ai.service.js +309 -88
  34. package/dist/ai/ai.service.js.map +1 -1
  35. package/dist/ai/dto/chat.dto.d.ts +4 -1
  36. package/dist/ai/dto/chat.dto.d.ts.map +1 -1
  37. package/dist/ai/dto/chat.dto.js +23 -1
  38. package/dist/ai/dto/chat.dto.js.map +1 -1
  39. package/dist/ai/dto/create-agent.dto.d.ts +1 -1
  40. package/dist/ai/dto/create-agent.dto.d.ts.map +1 -1
  41. package/dist/ai/dto/create-agent.dto.js +1 -1
  42. package/dist/ai/dto/create-agent.dto.js.map +1 -1
  43. package/dist/ai/dto/richtext-edit-html.dto.d.ts +5 -0
  44. package/dist/ai/dto/richtext-edit-html.dto.d.ts.map +1 -0
  45. package/dist/ai/dto/richtext-edit-html.dto.js +27 -0
  46. package/dist/ai/dto/richtext-edit-html.dto.js.map +1 -0
  47. package/dist/ai/dto/update-agent.dto.d.ts +1 -1
  48. package/dist/ai/dto/update-agent.dto.d.ts.map +1 -1
  49. package/dist/ai/dto/update-agent.dto.js +1 -1
  50. package/dist/ai/dto/update-agent.dto.js.map +1 -1
  51. package/dist/ai/richtext-ai.prompts.d.ts +5 -0
  52. package/dist/ai/richtext-ai.prompts.d.ts.map +1 -0
  53. package/dist/ai/richtext-ai.prompts.js +57 -0
  54. package/dist/ai/richtext-ai.prompts.js.map +1 -0
  55. package/dist/ai/richtext-ai.service.d.ts +34 -0
  56. package/dist/ai/richtext-ai.service.d.ts.map +1 -0
  57. package/dist/ai/richtext-ai.service.js +95 -0
  58. package/dist/ai/richtext-ai.service.js.map +1 -0
  59. package/dist/ai-instruction/ai-instruction.controller.d.ts +59 -5
  60. package/dist/ai-instruction/ai-instruction.controller.d.ts.map +1 -1
  61. package/dist/ai-instruction/ai-instruction.controller.js +63 -20
  62. package/dist/ai-instruction/ai-instruction.controller.js.map +1 -1
  63. package/dist/ai-instruction/ai-instruction.module.d.ts.map +1 -1
  64. package/dist/ai-instruction/ai-instruction.module.js +13 -2
  65. package/dist/ai-instruction/ai-instruction.module.js.map +1 -1
  66. package/dist/ai-instruction/ai-instruction.service.d.ts +131 -6
  67. package/dist/ai-instruction/ai-instruction.service.d.ts.map +1 -1
  68. package/dist/ai-instruction/ai-instruction.service.js +533 -60
  69. package/dist/ai-instruction/ai-instruction.service.js.map +1 -1
  70. package/dist/ai-instruction/ai-prompt-builder.service.d.ts +8 -0
  71. package/dist/ai-instruction/ai-prompt-builder.service.d.ts.map +1 -1
  72. package/dist/ai-instruction/ai-prompt-builder.service.js +10 -1
  73. package/dist/ai-instruction/ai-prompt-builder.service.js.map +1 -1
  74. package/dist/ai-instruction/ai-prompt-registry.d.ts +45 -0
  75. package/dist/ai-instruction/ai-prompt-registry.d.ts.map +1 -0
  76. package/dist/ai-instruction/ai-prompt-registry.js +49 -0
  77. package/dist/ai-instruction/ai-prompt-registry.js.map +1 -0
  78. package/dist/ai-instruction/ai-prompt-resolver.service.d.ts +89 -0
  79. package/dist/ai-instruction/ai-prompt-resolver.service.d.ts.map +1 -0
  80. package/dist/ai-instruction/ai-prompt-resolver.service.js +222 -0
  81. package/dist/ai-instruction/ai-prompt-resolver.service.js.map +1 -0
  82. package/dist/ai-instruction/ai-prompt-template.util.d.ts +70 -0
  83. package/dist/ai-instruction/ai-prompt-template.util.d.ts.map +1 -0
  84. package/dist/ai-instruction/ai-prompt-template.util.js +154 -0
  85. package/dist/ai-instruction/ai-prompt-template.util.js.map +1 -0
  86. package/dist/ai-instruction/dto/create-ai-instruction.dto.d.ts +5 -0
  87. package/dist/ai-instruction/dto/create-ai-instruction.dto.d.ts.map +1 -1
  88. package/dist/ai-instruction/dto/create-ai-instruction.dto.js +27 -1
  89. package/dist/ai-instruction/dto/create-ai-instruction.dto.js.map +1 -1
  90. package/dist/ai-instruction/dto/preview-ai-instruction.dto.d.ts +14 -0
  91. package/dist/ai-instruction/dto/preview-ai-instruction.dto.d.ts.map +1 -0
  92. package/dist/ai-instruction/dto/preview-ai-instruction.dto.js +42 -0
  93. package/dist/ai-instruction/dto/preview-ai-instruction.dto.js.map +1 -0
  94. package/dist/ai-instruction/dto/update-ai-instruction.dto.d.ts +11 -0
  95. package/dist/ai-instruction/dto/update-ai-instruction.dto.d.ts.map +1 -1
  96. package/dist/ai-instruction/dto/update-ai-instruction.dto.js +34 -0
  97. package/dist/ai-instruction/dto/update-ai-instruction.dto.js.map +1 -1
  98. package/dist/ai-instruction/types/ai-instruction.types.d.ts +13 -1
  99. package/dist/ai-instruction/types/ai-instruction.types.d.ts.map +1 -1
  100. package/dist/ai-instruction/types/ai-instruction.types.js +10 -0
  101. package/dist/ai-instruction/types/ai-instruction.types.js.map +1 -1
  102. package/dist/auth/auth.controller.d.ts +89 -15
  103. package/dist/auth/auth.controller.d.ts.map +1 -1
  104. package/dist/auth/auth.controller.js +116 -44
  105. package/dist/auth/auth.controller.js.map +1 -1
  106. package/dist/auth/auth.module.d.ts.map +1 -1
  107. package/dist/auth/auth.module.js +2 -0
  108. package/dist/auth/auth.module.js.map +1 -1
  109. package/dist/auth/auth.service.d.ts +169 -12
  110. package/dist/auth/auth.service.d.ts.map +1 -1
  111. package/dist/auth/auth.service.js +314 -67
  112. package/dist/auth/auth.service.js.map +1 -1
  113. package/dist/auth/dto/confirm-email.dto.d.ts +4 -0
  114. package/dist/auth/dto/confirm-email.dto.d.ts.map +1 -0
  115. package/dist/auth/dto/confirm-email.dto.js +22 -0
  116. package/dist/auth/dto/confirm-email.dto.js.map +1 -0
  117. package/dist/auth/dto/resend-confirmation.dto.d.ts +8 -0
  118. package/dist/auth/dto/resend-confirmation.dto.d.ts.map +1 -0
  119. package/dist/auth/dto/resend-confirmation.dto.js +36 -0
  120. package/dist/auth/dto/resend-confirmation.dto.js.map +1 -0
  121. package/dist/authorize/authorize.controller.d.ts +1 -1
  122. package/dist/authorize/authorize.controller.d.ts.map +1 -1
  123. package/dist/authorize/authorize.controller.js +4 -2
  124. package/dist/authorize/authorize.controller.js.map +1 -1
  125. package/dist/authorize/authorize.service.d.ts +7 -0
  126. package/dist/authorize/authorize.service.d.ts.map +1 -1
  127. package/dist/authorize/authorize.service.js +1 -1
  128. package/dist/authorize/authorize.service.js.map +1 -1
  129. package/dist/captcha/captcha.module.d.ts +3 -0
  130. package/dist/captcha/captcha.module.d.ts.map +1 -0
  131. package/dist/captcha/captcha.module.js +23 -0
  132. package/dist/captcha/captcha.module.js.map +1 -0
  133. package/dist/captcha/captcha.service.d.ts +38 -0
  134. package/dist/captcha/captcha.service.d.ts.map +1 -0
  135. package/dist/captcha/captcha.service.js +144 -0
  136. package/dist/captcha/captcha.service.js.map +1 -0
  137. package/dist/challenge/challenge.service.d.ts +31 -9
  138. package/dist/challenge/challenge.service.d.ts.map +1 -1
  139. package/dist/challenge/challenge.service.js +111 -16
  140. package/dist/challenge/challenge.service.js.map +1 -1
  141. package/dist/core.module.d.ts +3 -1
  142. package/dist/core.module.d.ts.map +1 -1
  143. package/dist/core.module.js +46 -2
  144. package/dist/core.module.js.map +1 -1
  145. package/dist/dashboard/dashboard/dashboard.controller.d.ts +21 -21
  146. package/dist/dashboard/dashboard/dashboard.service.d.ts +21 -21
  147. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +10 -10
  148. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +10 -10
  149. package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +20 -20
  150. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +20 -20
  151. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +3 -3
  152. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +3 -3
  153. package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts +2 -2
  154. package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts +2 -2
  155. package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts +13 -13
  156. package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts +13 -13
  157. package/dist/file/file.controller.d.ts +8 -1
  158. package/dist/file/file.controller.d.ts.map +1 -1
  159. package/dist/file/file.controller.js +45 -7
  160. package/dist/file/file.controller.js.map +1 -1
  161. package/dist/file/file.service.d.ts +134 -1
  162. package/dist/file/file.service.d.ts.map +1 -1
  163. package/dist/file/file.service.js +363 -12
  164. package/dist/file/file.service.js.map +1 -1
  165. package/dist/file/image-optimization.service.d.ts +43 -0
  166. package/dist/file/image-optimization.service.d.ts.map +1 -1
  167. package/dist/file/image-optimization.service.js +154 -0
  168. package/dist/file/image-optimization.service.js.map +1 -1
  169. package/dist/file/provider/abstract.provider.d.ts +16 -0
  170. package/dist/file/provider/abstract.provider.d.ts.map +1 -1
  171. package/dist/file/provider/abstract.provider.js +4 -0
  172. package/dist/file/provider/abstract.provider.js.map +1 -1
  173. package/dist/file/provider/gcs.provider.d.ts +2 -0
  174. package/dist/file/provider/gcs.provider.d.ts.map +1 -1
  175. package/dist/file/provider/gcs.provider.js +10 -0
  176. package/dist/file/provider/gcs.provider.js.map +1 -1
  177. package/dist/file/provider/local.provider.d.ts +1 -0
  178. package/dist/file/provider/local.provider.d.ts.map +1 -1
  179. package/dist/file/provider/local.provider.js +13 -0
  180. package/dist/file/provider/local.provider.js.map +1 -1
  181. package/dist/file/provider/s3.provider.d.ts +5 -0
  182. package/dist/file/provider/s3.provider.d.ts.map +1 -1
  183. package/dist/file/provider/s3.provider.js +15 -0
  184. package/dist/file/provider/s3.provider.js.map +1 -1
  185. package/dist/file/provider/spaces.provider.d.ts +5 -0
  186. package/dist/file/provider/spaces.provider.d.ts.map +1 -1
  187. package/dist/file/provider/spaces.provider.js +16 -0
  188. package/dist/file/provider/spaces.provider.js.map +1 -1
  189. package/dist/impersonation/dto/redeem-impersonation.dto.d.ts +4 -0
  190. package/dist/impersonation/dto/redeem-impersonation.dto.d.ts.map +1 -0
  191. package/dist/impersonation/dto/redeem-impersonation.dto.js +23 -0
  192. package/dist/impersonation/dto/redeem-impersonation.dto.js.map +1 -0
  193. package/dist/impersonation/dto/start-impersonation.dto.d.ts +7 -0
  194. package/dist/impersonation/dto/start-impersonation.dto.d.ts.map +1 -0
  195. package/dist/impersonation/dto/start-impersonation.dto.js +27 -0
  196. package/dist/impersonation/dto/start-impersonation.dto.js.map +1 -0
  197. package/dist/impersonation/impersonation-restrictions.d.ts +26 -0
  198. package/dist/impersonation/impersonation-restrictions.d.ts.map +1 -0
  199. package/dist/impersonation/impersonation-restrictions.js +76 -0
  200. package/dist/impersonation/impersonation-restrictions.js.map +1 -0
  201. package/dist/impersonation/impersonation.controller.d.ts +90 -0
  202. package/dist/impersonation/impersonation.controller.d.ts.map +1 -0
  203. package/dist/impersonation/impersonation.controller.js +135 -0
  204. package/dist/impersonation/impersonation.controller.js.map +1 -0
  205. package/dist/impersonation/impersonation.module.d.ts +8 -0
  206. package/dist/impersonation/impersonation.module.d.ts.map +1 -0
  207. package/dist/impersonation/impersonation.module.js +44 -0
  208. package/dist/impersonation/impersonation.module.js.map +1 -0
  209. package/dist/impersonation/impersonation.service.d.ts +164 -0
  210. package/dist/impersonation/impersonation.service.d.ts.map +1 -0
  211. package/dist/impersonation/impersonation.service.js +408 -0
  212. package/dist/impersonation/impersonation.service.js.map +1 -0
  213. package/dist/incident/incident-notifier.service.d.ts +36 -0
  214. package/dist/incident/incident-notifier.service.d.ts.map +1 -0
  215. package/dist/incident/incident-notifier.service.js +208 -0
  216. package/dist/incident/incident-notifier.service.js.map +1 -0
  217. package/dist/incident/incident.controller.d.ts +23 -0
  218. package/dist/incident/incident.controller.d.ts.map +1 -0
  219. package/dist/incident/incident.controller.js +75 -0
  220. package/dist/incident/incident.controller.js.map +1 -0
  221. package/dist/incident/incident.module.d.ts +7 -0
  222. package/dist/incident/incident.module.d.ts.map +1 -0
  223. package/dist/incident/incident.module.js +32 -0
  224. package/dist/incident/incident.module.js.map +1 -0
  225. package/dist/incident/incident.service.d.ts +72 -0
  226. package/dist/incident/incident.service.d.ts.map +1 -0
  227. package/dist/incident/incident.service.js +184 -0
  228. package/dist/incident/incident.service.js.map +1 -0
  229. package/dist/index.d.ts +43 -0
  230. package/dist/index.d.ts.map +1 -1
  231. package/dist/index.js +49 -0
  232. package/dist/index.js.map +1 -1
  233. package/dist/integration/services/outbox-polling.coordinator.d.ts +2 -0
  234. package/dist/integration/services/outbox-polling.coordinator.d.ts.map +1 -1
  235. package/dist/integration/services/outbox-polling.coordinator.js +50 -30
  236. package/dist/integration/services/outbox-polling.coordinator.js.map +1 -1
  237. package/dist/integration/services/outbox.service.d.ts.map +1 -1
  238. package/dist/integration/services/outbox.service.js +5 -1
  239. package/dist/integration/services/outbox.service.js.map +1 -1
  240. package/dist/integration-profile/dto/test.dto.d.ts +13 -0
  241. package/dist/integration-profile/dto/test.dto.d.ts.map +1 -0
  242. package/dist/integration-profile/dto/test.dto.js +26 -0
  243. package/dist/integration-profile/dto/test.dto.js.map +1 -0
  244. package/dist/integration-profile/integration-profile.controller.d.ts +12 -1
  245. package/dist/integration-profile/integration-profile.controller.d.ts.map +1 -1
  246. package/dist/integration-profile/integration-profile.controller.js +9 -1
  247. package/dist/integration-profile/integration-profile.controller.js.map +1 -1
  248. package/dist/integration-profile/integration-profile.module.d.ts +6 -1
  249. package/dist/integration-profile/integration-profile.module.d.ts.map +1 -1
  250. package/dist/integration-profile/integration-profile.module.js +26 -3
  251. package/dist/integration-profile/integration-profile.module.js.map +1 -1
  252. package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -1
  253. package/dist/integration-profile/integration-profile.secrets.js +12 -1
  254. package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
  255. package/dist/integration-profile/integration-profile.service.d.ts +26 -7
  256. package/dist/integration-profile/integration-profile.service.d.ts.map +1 -1
  257. package/dist/integration-profile/integration-profile.service.js +94 -91
  258. package/dist/integration-profile/integration-profile.service.js.map +1 -1
  259. package/dist/integration-profile/integration-profile.utils.d.ts +27 -1
  260. package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -1
  261. package/dist/integration-profile/integration-profile.utils.js +46 -5
  262. package/dist/integration-profile/integration-profile.utils.js.map +1 -1
  263. package/dist/integration-profile/testers/ai.testers.d.ts +3 -0
  264. package/dist/integration-profile/testers/ai.testers.d.ts.map +1 -0
  265. package/dist/integration-profile/testers/ai.testers.js +125 -0
  266. package/dist/integration-profile/testers/ai.testers.js.map +1 -0
  267. package/dist/integration-profile/testers/captcha.testers.d.ts +3 -0
  268. package/dist/integration-profile/testers/captcha.testers.d.ts.map +1 -0
  269. package/dist/integration-profile/testers/captcha.testers.js +121 -0
  270. package/dist/integration-profile/testers/captcha.testers.js.map +1 -0
  271. package/dist/integration-profile/testers/email.testers.d.ts +3 -0
  272. package/dist/integration-profile/testers/email.testers.d.ts.map +1 -0
  273. package/dist/integration-profile/testers/email.testers.js +156 -0
  274. package/dist/integration-profile/testers/email.testers.js.map +1 -0
  275. package/dist/integration-profile/testers/http-probe.d.ts +57 -0
  276. package/dist/integration-profile/testers/http-probe.d.ts.map +1 -0
  277. package/dist/integration-profile/testers/http-probe.js +140 -0
  278. package/dist/integration-profile/testers/http-probe.js.map +1 -0
  279. package/dist/integration-profile/testers/index.d.ts +12 -0
  280. package/dist/integration-profile/testers/index.d.ts.map +1 -0
  281. package/dist/integration-profile/testers/index.js +45 -0
  282. package/dist/integration-profile/testers/index.js.map +1 -0
  283. package/dist/integration-profile/testers/infrastructure.testers.d.ts +3 -0
  284. package/dist/integration-profile/testers/infrastructure.testers.d.ts.map +1 -0
  285. package/dist/integration-profile/testers/infrastructure.testers.js +105 -0
  286. package/dist/integration-profile/testers/infrastructure.testers.js.map +1 -0
  287. package/dist/integration-profile/testers/integration-tester.registry.d.ts +17 -0
  288. package/dist/integration-profile/testers/integration-tester.registry.d.ts.map +1 -0
  289. package/dist/integration-profile/testers/integration-tester.registry.js +52 -0
  290. package/dist/integration-profile/testers/integration-tester.registry.js.map +1 -0
  291. package/dist/integration-profile/testers/integration-tester.types.d.ts +55 -0
  292. package/dist/integration-profile/testers/integration-tester.types.d.ts.map +1 -0
  293. package/dist/integration-profile/testers/integration-tester.types.js +16 -0
  294. package/dist/integration-profile/testers/integration-tester.types.js.map +1 -0
  295. package/dist/integration-profile/testers/messaging.testers.d.ts +3 -0
  296. package/dist/integration-profile/testers/messaging.testers.d.ts.map +1 -0
  297. package/dist/integration-profile/testers/messaging.testers.js +70 -0
  298. package/dist/integration-profile/testers/messaging.testers.js.map +1 -0
  299. package/dist/integration-profile/testers/oauth.testers.d.ts +3 -0
  300. package/dist/integration-profile/testers/oauth.testers.d.ts.map +1 -0
  301. package/dist/integration-profile/testers/oauth.testers.js +285 -0
  302. package/dist/integration-profile/testers/oauth.testers.js.map +1 -0
  303. package/dist/integration-profile/testers/payment.testers.d.ts +3 -0
  304. package/dist/integration-profile/testers/payment.testers.d.ts.map +1 -0
  305. package/dist/integration-profile/testers/payment.testers.js +169 -0
  306. package/dist/integration-profile/testers/payment.testers.js.map +1 -0
  307. package/dist/integration-profile/testers/storage.testers.d.ts +3 -0
  308. package/dist/integration-profile/testers/storage.testers.d.ts.map +1 -0
  309. package/dist/integration-profile/testers/storage.testers.js +208 -0
  310. package/dist/integration-profile/testers/storage.testers.js.map +1 -0
  311. package/dist/language/en.json +15 -2
  312. package/dist/language/pt.json +15 -2
  313. package/dist/leaked-password/leaked-password.controller.d.ts +24 -0
  314. package/dist/leaked-password/leaked-password.controller.d.ts.map +1 -0
  315. package/dist/leaked-password/leaked-password.controller.js +78 -0
  316. package/dist/leaked-password/leaked-password.controller.js.map +1 -0
  317. package/dist/leaked-password/leaked-password.module.d.ts +8 -0
  318. package/dist/leaked-password/leaked-password.module.d.ts.map +1 -0
  319. package/dist/leaked-password/leaked-password.module.js +31 -0
  320. package/dist/leaked-password/leaked-password.module.js.map +1 -0
  321. package/dist/leaked-password/leaked-password.service.d.ts +64 -0
  322. package/dist/leaked-password/leaked-password.service.d.ts.map +1 -0
  323. package/dist/leaked-password/leaked-password.service.js +218 -0
  324. package/dist/leaked-password/leaked-password.service.js.map +1 -0
  325. package/dist/locale/locale.controller.d.ts +1 -1
  326. package/dist/locale/locale.service.d.ts +1 -1
  327. package/dist/mail/dto/preview.dto.d.ts +18 -0
  328. package/dist/mail/dto/preview.dto.d.ts.map +1 -0
  329. package/dist/mail/dto/preview.dto.js +66 -0
  330. package/dist/mail/dto/preview.dto.js.map +1 -0
  331. package/dist/mail/dto/send.dto.d.ts +44 -0
  332. package/dist/mail/dto/send.dto.d.ts.map +1 -1
  333. package/dist/mail/dto/send.dto.js +20 -0
  334. package/dist/mail/dto/send.dto.js.map +1 -1
  335. package/dist/mail/mail.controller.d.ts +5 -0
  336. package/dist/mail/mail.controller.d.ts.map +1 -1
  337. package/dist/mail/mail.controller.js +16 -2
  338. package/dist/mail/mail.controller.js.map +1 -1
  339. package/dist/mail/mail.module.d.ts.map +1 -1
  340. package/dist/mail/mail.module.js +2 -0
  341. package/dist/mail/mail.module.js.map +1 -1
  342. package/dist/mail/mail.service.d.ts +69 -4
  343. package/dist/mail/mail.service.d.ts.map +1 -1
  344. package/dist/mail/mail.service.js +278 -37
  345. package/dist/mail/mail.service.js.map +1 -1
  346. package/dist/mail-delivery/mail-delivery-provision.service.d.ts +21 -0
  347. package/dist/mail-delivery/mail-delivery-provision.service.d.ts.map +1 -0
  348. package/dist/mail-delivery/mail-delivery-provision.service.js +64 -0
  349. package/dist/mail-delivery/mail-delivery-provision.service.js.map +1 -0
  350. package/dist/mail-delivery/mail-delivery.constants.d.ts +17 -0
  351. package/dist/mail-delivery/mail-delivery.constants.d.ts.map +1 -0
  352. package/dist/mail-delivery/mail-delivery.constants.js +31 -0
  353. package/dist/mail-delivery/mail-delivery.constants.js.map +1 -0
  354. package/dist/mail-delivery/mail-delivery.controller.d.ts +19 -0
  355. package/dist/mail-delivery/mail-delivery.controller.d.ts.map +1 -0
  356. package/dist/mail-delivery/mail-delivery.controller.js +53 -0
  357. package/dist/mail-delivery/mail-delivery.controller.js.map +1 -0
  358. package/dist/mail-delivery/mail-delivery.module.d.ts +7 -0
  359. package/dist/mail-delivery/mail-delivery.module.d.ts.map +1 -0
  360. package/dist/mail-delivery/mail-delivery.module.js +36 -0
  361. package/dist/mail-delivery/mail-delivery.module.js.map +1 -0
  362. package/dist/mail-delivery/mail-delivery.service.d.ts +38 -0
  363. package/dist/mail-delivery/mail-delivery.service.d.ts.map +1 -0
  364. package/dist/mail-delivery/mail-delivery.service.js +140 -0
  365. package/dist/mail-delivery/mail-delivery.service.js.map +1 -0
  366. package/dist/mail-delivery/ses-event.parser.d.ts +27 -0
  367. package/dist/mail-delivery/ses-event.parser.d.ts.map +1 -0
  368. package/dist/mail-delivery/ses-event.parser.js +131 -0
  369. package/dist/mail-delivery/ses-event.parser.js.map +1 -0
  370. package/dist/mail-delivery/ses-webhook.service.d.ts +58 -0
  371. package/dist/mail-delivery/ses-webhook.service.d.ts.map +1 -0
  372. package/dist/mail-delivery/ses-webhook.service.js +214 -0
  373. package/dist/mail-delivery/ses-webhook.service.js.map +1 -0
  374. package/dist/mail-delivery/sns-message.util.d.ts +38 -0
  375. package/dist/mail-delivery/sns-message.util.d.ts.map +1 -0
  376. package/dist/mail-delivery/sns-message.util.js +109 -0
  377. package/dist/mail-delivery/sns-message.util.js.map +1 -0
  378. package/dist/mail-layout/dto/update.dto.d.ts +17 -0
  379. package/dist/mail-layout/dto/update.dto.d.ts.map +1 -0
  380. package/dist/mail-layout/dto/update.dto.js +27 -0
  381. package/dist/mail-layout/dto/update.dto.js.map +1 -0
  382. package/dist/mail-layout/mail-layout.controller.d.ts +15 -0
  383. package/dist/mail-layout/mail-layout.controller.d.ts.map +1 -0
  384. package/dist/mail-layout/mail-layout.controller.js +71 -0
  385. package/dist/mail-layout/mail-layout.controller.js.map +1 -0
  386. package/dist/mail-layout/mail-layout.module.d.ts +3 -0
  387. package/dist/mail-layout/mail-layout.module.d.ts.map +1 -0
  388. package/dist/mail-layout/mail-layout.module.js +35 -0
  389. package/dist/mail-layout/mail-layout.module.js.map +1 -0
  390. package/dist/mail-layout/mail-layout.service.d.ts +27 -0
  391. package/dist/mail-layout/mail-layout.service.d.ts.map +1 -0
  392. package/dist/mail-layout/mail-layout.service.js +80 -0
  393. package/dist/mail-layout/mail-layout.service.js.map +1 -0
  394. package/dist/mail-sent/dto/query-mail-sent-list.dto.d.ts +5 -0
  395. package/dist/mail-sent/dto/query-mail-sent-list.dto.d.ts.map +1 -1
  396. package/dist/mail-sent/dto/query-mail-sent-list.dto.js +6 -0
  397. package/dist/mail-sent/dto/query-mail-sent-list.dto.js.map +1 -1
  398. package/dist/mail-sent/mail-sent.controller.d.ts +39 -0
  399. package/dist/mail-sent/mail-sent.controller.d.ts.map +1 -1
  400. package/dist/mail-sent/mail-sent.service.d.ts +39 -0
  401. package/dist/mail-sent/mail-sent.service.d.ts.map +1 -1
  402. package/dist/mail-sent/mail-sent.service.js +45 -4
  403. package/dist/mail-sent/mail-sent.service.js.map +1 -1
  404. package/dist/mailbox/mailbox-fetcher.registry.d.ts +100 -0
  405. package/dist/mailbox/mailbox-fetcher.registry.d.ts.map +1 -0
  406. package/dist/mailbox/mailbox-fetcher.registry.js +59 -0
  407. package/dist/mailbox/mailbox-fetcher.registry.js.map +1 -0
  408. package/dist/mailbox/mailbox.module.d.ts +8 -0
  409. package/dist/mailbox/mailbox.module.d.ts.map +1 -0
  410. package/dist/mailbox/mailbox.module.js +27 -0
  411. package/dist/mailbox/mailbox.module.js.map +1 -0
  412. package/dist/mcp-chat/mcp-chat.controller.d.ts +2 -2
  413. package/dist/mcp-chat/mcp-chat.service.d.ts +24 -2
  414. package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
  415. package/dist/mcp-chat/mcp-chat.service.js +80 -24
  416. package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
  417. package/dist/meeting/meeting-provider.registry.d.ts +155 -0
  418. package/dist/meeting/meeting-provider.registry.d.ts.map +1 -0
  419. package/dist/meeting/meeting-provider.registry.js +53 -0
  420. package/dist/meeting/meeting-provider.registry.js.map +1 -0
  421. package/dist/meeting/meeting.constants.d.ts +47 -0
  422. package/dist/meeting/meeting.constants.d.ts.map +1 -0
  423. package/dist/meeting/meeting.constants.js +21 -0
  424. package/dist/meeting/meeting.constants.js.map +1 -0
  425. package/dist/meeting/meeting.module.d.ts +7 -0
  426. package/dist/meeting/meeting.module.d.ts.map +1 -0
  427. package/dist/meeting/meeting.module.js +26 -0
  428. package/dist/meeting/meeting.module.js.map +1 -0
  429. package/dist/menu/menu.controller.d.ts +3 -3
  430. package/dist/menu/menu.service.d.ts +3 -3
  431. package/dist/middlewares/locale-injection.middleware.d.ts +12 -0
  432. package/dist/middlewares/locale-injection.middleware.d.ts.map +1 -1
  433. package/dist/middlewares/locale-injection.middleware.js +20 -4
  434. package/dist/middlewares/locale-injection.middleware.js.map +1 -1
  435. package/dist/notification/notification.controller.d.ts +12 -12
  436. package/dist/notification/notification.service.d.ts +12 -12
  437. package/dist/oauth/oauth-token-broker.service.d.ts +72 -0
  438. package/dist/oauth/oauth-token-broker.service.d.ts.map +1 -0
  439. package/dist/oauth/oauth-token-broker.service.js +258 -0
  440. package/dist/oauth/oauth-token-broker.service.js.map +1 -0
  441. package/dist/oauth/oauth.module.d.ts.map +1 -1
  442. package/dist/oauth/oauth.module.js +3 -1
  443. package/dist/oauth/oauth.module.js.map +1 -1
  444. package/dist/oauth/oauth.service.d.ts +2 -0
  445. package/dist/oauth/oauth.service.d.ts.map +1 -1
  446. package/dist/oauth/oauth.service.js +6 -3
  447. package/dist/oauth/oauth.service.js.map +1 -1
  448. package/dist/oauth/providers/google.provider.d.ts +7 -0
  449. package/dist/oauth/providers/google.provider.d.ts.map +1 -1
  450. package/dist/oauth/providers/google.provider.js +43 -13
  451. package/dist/oauth/providers/google.provider.js.map +1 -1
  452. package/dist/observability/monitored-cron.d.ts +34 -0
  453. package/dist/observability/monitored-cron.d.ts.map +1 -0
  454. package/dist/observability/monitored-cron.js +118 -0
  455. package/dist/observability/monitored-cron.js.map +1 -0
  456. package/dist/pairing/dto/approve-reverse-pairing.dto.d.ts +12 -0
  457. package/dist/pairing/dto/approve-reverse-pairing.dto.d.ts.map +1 -0
  458. package/dist/pairing/dto/approve-reverse-pairing.dto.js +30 -0
  459. package/dist/pairing/dto/approve-reverse-pairing.dto.js.map +1 -0
  460. package/dist/pairing/dto/claim-pairing.dto.d.ts +11 -0
  461. package/dist/pairing/dto/claim-pairing.dto.d.ts.map +1 -0
  462. package/dist/pairing/dto/claim-pairing.dto.js +35 -0
  463. package/dist/pairing/dto/claim-pairing.dto.js.map +1 -0
  464. package/dist/pairing/dto/confirm-pairing.dto.d.ts +10 -0
  465. package/dist/pairing/dto/confirm-pairing.dto.d.ts.map +1 -0
  466. package/dist/pairing/dto/confirm-pairing.dto.js +24 -0
  467. package/dist/pairing/dto/confirm-pairing.dto.js.map +1 -0
  468. package/dist/pairing/dto/poll-reverse-pairing.dto.d.ts +5 -0
  469. package/dist/pairing/dto/poll-reverse-pairing.dto.d.ts.map +1 -0
  470. package/dist/pairing/dto/poll-reverse-pairing.dto.js +24 -0
  471. package/dist/pairing/dto/poll-reverse-pairing.dto.js.map +1 -0
  472. package/dist/pairing/dto/start-pairing.dto.d.ts +11 -0
  473. package/dist/pairing/dto/start-pairing.dto.d.ts.map +1 -0
  474. package/dist/pairing/dto/start-pairing.dto.js +30 -0
  475. package/dist/pairing/dto/start-pairing.dto.js.map +1 -0
  476. package/dist/pairing/dto/start-reverse-pairing.dto.d.ts +24 -0
  477. package/dist/pairing/dto/start-reverse-pairing.dto.d.ts.map +1 -0
  478. package/dist/pairing/dto/start-reverse-pairing.dto.js +47 -0
  479. package/dist/pairing/dto/start-reverse-pairing.dto.js.map +1 -0
  480. package/dist/pairing/pairing.controller.d.ts +118 -0
  481. package/dist/pairing/pairing.controller.d.ts.map +1 -0
  482. package/dist/pairing/pairing.controller.js +296 -0
  483. package/dist/pairing/pairing.controller.js.map +1 -0
  484. package/dist/pairing/pairing.module.d.ts +10 -0
  485. package/dist/pairing/pairing.module.d.ts.map +1 -0
  486. package/dist/pairing/pairing.module.js +40 -0
  487. package/dist/pairing/pairing.module.js.map +1 -0
  488. package/dist/pairing/pairing.service.d.ts +238 -0
  489. package/dist/pairing/pairing.service.d.ts.map +1 -0
  490. package/dist/pairing/pairing.service.js +495 -0
  491. package/dist/pairing/pairing.service.js.map +1 -0
  492. package/dist/pdf/pdf.service.d.ts +11 -6
  493. package/dist/pdf/pdf.service.d.ts.map +1 -1
  494. package/dist/pdf/pdf.service.js +60 -20
  495. package/dist/pdf/pdf.service.js.map +1 -1
  496. package/dist/presence/presence.controller.d.ts +16 -0
  497. package/dist/presence/presence.controller.d.ts.map +1 -0
  498. package/dist/presence/presence.controller.js +60 -0
  499. package/dist/presence/presence.controller.js.map +1 -0
  500. package/dist/presence/presence.listener.d.ts +29 -0
  501. package/dist/presence/presence.listener.d.ts.map +1 -0
  502. package/dist/presence/presence.listener.js +82 -0
  503. package/dist/presence/presence.listener.js.map +1 -0
  504. package/dist/presence/presence.module.d.ts +3 -0
  505. package/dist/presence/presence.module.d.ts.map +1 -0
  506. package/dist/presence/presence.module.js +32 -0
  507. package/dist/presence/presence.module.js.map +1 -0
  508. package/dist/presence/presence.service.d.ts +42 -0
  509. package/dist/presence/presence.service.d.ts.map +1 -0
  510. package/dist/presence/presence.service.js +147 -0
  511. package/dist/presence/presence.service.js.map +1 -0
  512. package/dist/profile/dto/delete-account.dto.d.ts +8 -0
  513. package/dist/profile/dto/delete-account.dto.d.ts.map +1 -0
  514. package/dist/profile/dto/delete-account.dto.js +22 -0
  515. package/dist/profile/dto/delete-account.dto.js.map +1 -0
  516. package/dist/profile/events/profile-account-deleted.event.d.ts +12 -0
  517. package/dist/profile/events/profile-account-deleted.event.d.ts.map +1 -0
  518. package/dist/profile/events/profile-account-deleted.event.js +17 -0
  519. package/dist/profile/events/profile-account-deleted.event.js.map +1 -0
  520. package/dist/profile/profile.controller.d.ts +32 -15
  521. package/dist/profile/profile.controller.d.ts.map +1 -1
  522. package/dist/profile/profile.controller.js +75 -46
  523. package/dist/profile/profile.controller.js.map +1 -1
  524. package/dist/profile/profile.service.d.ts +41 -18
  525. package/dist/profile/profile.service.d.ts.map +1 -1
  526. package/dist/profile/profile.service.js +133 -48
  527. package/dist/profile/profile.service.js.map +1 -1
  528. package/dist/realtime/realtime.gateway.d.ts +48 -0
  529. package/dist/realtime/realtime.gateway.d.ts.map +1 -0
  530. package/dist/realtime/realtime.gateway.js +156 -0
  531. package/dist/realtime/realtime.gateway.js.map +1 -0
  532. package/dist/realtime/realtime.module.d.ts +8 -0
  533. package/dist/realtime/realtime.module.d.ts.map +1 -0
  534. package/dist/realtime/realtime.module.js +29 -0
  535. package/dist/realtime/realtime.module.js.map +1 -0
  536. package/dist/realtime/realtime.service.d.ts +51 -0
  537. package/dist/realtime/realtime.service.d.ts.map +1 -0
  538. package/dist/realtime/realtime.service.js +177 -0
  539. package/dist/realtime/realtime.service.js.map +1 -0
  540. package/dist/redis/redis-pubsub.d.ts +27 -0
  541. package/dist/redis/redis-pubsub.d.ts.map +1 -0
  542. package/dist/redis/redis-pubsub.js +184 -0
  543. package/dist/redis/redis-pubsub.js.map +1 -0
  544. package/dist/role/guards/role.guard.d.ts +5 -0
  545. package/dist/role/guards/role.guard.d.ts.map +1 -1
  546. package/dist/role/guards/role.guard.js +41 -39
  547. package/dist/role/guards/role.guard.js.map +1 -1
  548. package/dist/role/role.controller.d.ts +3 -3
  549. package/dist/role/role.service.d.ts +3 -3
  550. package/dist/session/session.controller.d.ts +2 -0
  551. package/dist/session/session.controller.d.ts.map +1 -1
  552. package/dist/session/session.service.d.ts +39 -0
  553. package/dist/session/session.service.d.ts.map +1 -1
  554. package/dist/session/session.service.js +91 -8
  555. package/dist/session/session.service.js.map +1 -1
  556. package/dist/setting/brand-asset.service.d.ts +48 -0
  557. package/dist/setting/brand-asset.service.d.ts.map +1 -0
  558. package/dist/setting/brand-asset.service.js +130 -0
  559. package/dist/setting/brand-asset.service.js.map +1 -0
  560. package/dist/setting/dto/create.dto.d.ts.map +1 -1
  561. package/dist/setting/dto/create.dto.js +9 -2
  562. package/dist/setting/dto/create.dto.js.map +1 -1
  563. package/dist/setting/setting.controller.d.ts +5 -5
  564. package/dist/setting/setting.module.d.ts.map +1 -1
  565. package/dist/setting/setting.module.js +3 -2
  566. package/dist/setting/setting.module.js.map +1 -1
  567. package/dist/setting/setting.service.d.ts +41 -5
  568. package/dist/setting/setting.service.d.ts.map +1 -1
  569. package/dist/setting/setting.service.js +113 -1
  570. package/dist/setting/setting.service.js.map +1 -1
  571. package/dist/token/token.service.d.ts +23 -2
  572. package/dist/token/token.service.d.ts.map +1 -1
  573. package/dist/token/token.service.js +39 -4
  574. package/dist/token/token.service.js.map +1 -1
  575. package/dist/user/dto/create-with-email-and-password.dto.d.ts +2 -0
  576. package/dist/user/dto/create-with-email-and-password.dto.d.ts.map +1 -1
  577. package/dist/user/dto/create-with-email-and-password.dto.js +5 -0
  578. package/dist/user/dto/create-with-email-and-password.dto.js.map +1 -1
  579. package/dist/user/dto/query-user-mail-list.dto.d.ts +5 -0
  580. package/dist/user/dto/query-user-mail-list.dto.d.ts.map +1 -0
  581. package/dist/user/dto/query-user-mail-list.dto.js +25 -0
  582. package/dist/user/dto/query-user-mail-list.dto.js.map +1 -0
  583. package/dist/user/user-credential.util.d.ts +19 -0
  584. package/dist/user/user-credential.util.d.ts.map +1 -0
  585. package/dist/user/user-credential.util.js +32 -0
  586. package/dist/user/user-credential.util.js.map +1 -0
  587. package/dist/user/user.controller.d.ts +24 -26
  588. package/dist/user/user.controller.d.ts.map +1 -1
  589. package/dist/user/user.controller.js +18 -3
  590. package/dist/user/user.controller.js.map +1 -1
  591. package/dist/user/user.service.d.ts +71 -29
  592. package/dist/user/user.service.d.ts.map +1 -1
  593. package/dist/user/user.service.js +291 -17
  594. package/dist/user/user.service.js.map +1 -1
  595. package/dist/user-device/user-device.controller.d.ts +54 -0
  596. package/dist/user-device/user-device.controller.d.ts.map +1 -0
  597. package/dist/user-device/user-device.controller.js +103 -0
  598. package/dist/user-device/user-device.controller.js.map +1 -0
  599. package/dist/user-device/user-device.module.d.ts +3 -0
  600. package/dist/user-device/user-device.module.d.ts.map +1 -0
  601. package/dist/user-device/user-device.module.js +26 -0
  602. package/dist/user-device/user-device.module.js.map +1 -0
  603. package/dist/user-device/user-device.service.d.ts +71 -0
  604. package/dist/user-device/user-device.service.d.ts.map +1 -0
  605. package/dist/user-device/user-device.service.js +158 -0
  606. package/dist/user-device/user-device.service.js.map +1 -0
  607. package/dist/utils/device-key.d.ts +23 -0
  608. package/dist/utils/device-key.d.ts.map +1 -0
  609. package/dist/utils/device-key.js +31 -0
  610. package/dist/utils/device-key.js.map +1 -0
  611. package/dist/utils/geo-ip.d.ts +38 -0
  612. package/dist/utils/geo-ip.d.ts.map +1 -0
  613. package/dist/utils/geo-ip.js +122 -0
  614. package/dist/utils/geo-ip.js.map +1 -0
  615. package/dist/utils/locale-context.d.ts +10 -7
  616. package/dist/utils/locale-context.d.ts.map +1 -1
  617. package/dist/utils/locale-context.js +14 -8
  618. package/dist/utils/locale-context.js.map +1 -1
  619. package/dist/utils/security-event-vars.d.ts +56 -0
  620. package/dist/utils/security-event-vars.d.ts.map +1 -0
  621. package/dist/utils/security-event-vars.js +109 -0
  622. package/dist/utils/security-event-vars.js.map +1 -0
  623. package/dist/utils/user-agent.d.ts +19 -0
  624. package/dist/utils/user-agent.d.ts.map +1 -0
  625. package/dist/utils/user-agent.js +36 -0
  626. package/dist/utils/user-agent.js.map +1 -0
  627. package/dist/validators/is-strong-password-with-settings.validator.d.ts.map +1 -1
  628. package/dist/validators/is-strong-password-with-settings.validator.js +11 -0
  629. package/dist/validators/is-strong-password-with-settings.validator.js.map +1 -1
  630. package/dist/validators/service-locator.d.ts +9 -0
  631. package/dist/validators/service-locator.d.ts.map +1 -1
  632. package/dist/validators/service-locator.js +12 -0
  633. package/dist/validators/service-locator.js.map +1 -1
  634. package/dist/webhook-integration/webhook-integration.service.d.ts +5 -0
  635. package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
  636. package/dist/webhook-integration/webhook-integration.service.js +50 -4
  637. package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
  638. package/dist/webhook-integration/webhook-protocol.registry.d.ts +6 -0
  639. package/dist/webhook-integration/webhook-protocol.registry.d.ts.map +1 -1
  640. package/dist/webhook-integration/webhook-protocol.registry.js.map +1 -1
  641. package/dist/whatsapp-sender/whatsapp-sender.module.d.ts +3 -0
  642. package/dist/whatsapp-sender/whatsapp-sender.module.d.ts.map +1 -0
  643. package/dist/whatsapp-sender/whatsapp-sender.module.js +23 -0
  644. package/dist/whatsapp-sender/whatsapp-sender.module.js.map +1 -0
  645. package/dist/whatsapp-sender/whatsapp-sender.service.d.ts +50 -0
  646. package/dist/whatsapp-sender/whatsapp-sender.service.d.ts.map +1 -0
  647. package/dist/whatsapp-sender/whatsapp-sender.service.js +224 -0
  648. package/dist/whatsapp-sender/whatsapp-sender.service.js.map +1 -0
  649. package/hedhog/data/integration_provider.yaml +25 -0
  650. package/hedhog/data/mail.yaml +275 -112
  651. package/hedhog/data/mail_layout.yaml +198 -0
  652. package/hedhog/data/menu.yaml +69 -3
  653. package/hedhog/data/role.yaml +16 -0
  654. package/hedhog/data/route.yaml +307 -3
  655. package/hedhog/data/setting_group.yaml +315 -1
  656. package/hedhog/data/setting_subgroup.yaml +8 -0
  657. package/hedhog/frontend/app/_lib/hooks/use-core-access.test.tsx.ejs +125 -0
  658. package/hedhog/frontend/app/_lib/hooks/use-core-access.ts.ejs +29 -0
  659. package/hedhog/frontend/app/_lib/hooks/use-impersonation-access.test.ts.ejs +71 -0
  660. package/hedhog/frontend/app/_lib/hooks/use-impersonation-access.ts.ejs +32 -0
  661. package/hedhog/frontend/app/access-log/page.tsx.ejs +96 -22
  662. package/hedhog/frontend/app/account/components/active-sessions.tsx.ejs +17 -1
  663. package/hedhog/frontend/app/account/components/known-devices.tsx.ejs +89 -0
  664. package/hedhog/frontend/app/account/components/mfa-setup-dialog.tsx.ejs +36 -0
  665. package/hedhog/frontend/app/account/components/profile-form.tsx.ejs +2 -1
  666. package/hedhog/frontend/app/account/components/two-factor-auth.tsx.ejs +2 -0
  667. package/hedhog/frontend/app/account/components/user-devices-list.tsx.ejs +186 -0
  668. package/hedhog/frontend/app/account/devices/page.tsx.ejs +5 -0
  669. package/hedhog/frontend/app/account/hooks/use-mfa-setup.ts.ejs +10 -7
  670. package/hedhog/frontend/app/account/layout.tsx.ejs +2 -0
  671. package/hedhog/frontend/app/ai_agent/page.tsx.ejs +7 -4
  672. package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-form.tsx.ejs +580 -0
  673. package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-preview.tsx.ejs +150 -0
  674. package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-versions.tsx.ejs +199 -0
  675. package/hedhog/frontend/app/ai_instruction/_components/prompt-diff.tsx.ejs +63 -0
  676. package/hedhog/frontend/app/ai_instruction/_lib/line-diff.spec.ts.ejs +59 -0
  677. package/hedhog/frontend/app/ai_instruction/_lib/line-diff.ts.ejs +72 -0
  678. package/hedhog/frontend/app/ai_instruction/_lib/prompt-template.spec.ts.ejs +170 -0
  679. package/hedhog/frontend/app/ai_instruction/_lib/prompt-template.ts.ejs +167 -0
  680. package/hedhog/frontend/app/ai_instruction/_lib/types.ts.ejs +99 -0
  681. package/hedhog/frontend/app/ai_instruction/page.tsx.ejs +422 -0
  682. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.test.tsx.ejs +298 -0
  683. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +410 -1223
  684. package/hedhog/frontend/app/incidents/page.tsx.ejs +400 -0
  685. package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +153 -84
  686. package/hedhog/frontend/app/mail/layout/page.tsx.ejs +344 -0
  687. package/hedhog/frontend/app/mail/log/page.tsx.ejs +210 -3
  688. package/hedhog/frontend/app/mail/template/page.tsx.ejs +292 -200
  689. package/hedhog/frontend/app/mcp_chat/page.tsx.ejs +129 -17
  690. package/hedhog/frontend/app/pages/_components/page-delete-dialog.tsx.ejs +343 -0
  691. package/hedhog/frontend/app/pages/_components/page-detail-sheet.tsx.ejs +438 -0
  692. package/hedhog/frontend/app/pages/_components/page-wizard-sheet.tsx.ejs +1009 -0
  693. package/hedhog/frontend/app/pages/_components/system-page-types.ts.ejs +56 -0
  694. package/hedhog/frontend/app/pages/_components/use-system-pages.ts.ejs +171 -0
  695. package/hedhog/frontend/app/pages/page.tsx.ejs +514 -0
  696. package/hedhog/frontend/app/users/active-session.tsx.ejs +58 -11
  697. package/hedhog/frontend/app/users/page.tsx.ejs +186 -21
  698. package/hedhog/frontend/app/users/simulate-access-dialog.test.tsx.ejs +202 -0
  699. package/hedhog/frontend/app/users/simulate-access-dialog.tsx.ejs +170 -0
  700. package/hedhog/frontend/app/users/user-activity-timeline.tsx.ejs +250 -0
  701. package/hedhog/frontend/app/users/user-devices.tsx.ejs +89 -0
  702. package/hedhog/frontend/app/users/user-logs.tsx.ejs +13 -1
  703. package/hedhog/frontend/app/users/user-mails.tsx.ejs +399 -0
  704. package/hedhog/frontend/app/users/user-sheet.tsx.ejs +106 -99
  705. package/hedhog/frontend/messages/en.json +643 -49
  706. package/hedhog/frontend/messages/pt.json +643 -49
  707. package/hedhog/table/access_log.yaml +7 -0
  708. package/hedhog/table/ai_agent.yaml +1 -0
  709. package/hedhog/table/ai_instruction.yaml +15 -0
  710. package/hedhog/table/file.yaml +8 -0
  711. package/hedhog/table/leaked_password.yaml +18 -0
  712. package/hedhog/table/mail_delivery_event.yaml +76 -0
  713. package/hedhog/table/mail_layout.yaml +35 -0
  714. package/hedhog/table/mail_sent.yaml +23 -0
  715. package/hedhog/table/mail_var.yaml +4 -0
  716. package/hedhog/table/setting.yaml +1 -0
  717. package/hedhog/table/system_incident.yaml +59 -0
  718. package/hedhog/table/user_activity.yaml +9 -0
  719. package/hedhog/table/user_device.yaml +52 -0
  720. package/hedhog/table/user_presence.yaml +36 -0
  721. package/hedhog/table/user_session.yaml +20 -1
  722. package/package.json +7 -2
  723. package/src/access-log/access-log.controller.ts +1 -0
  724. package/src/access-log/access-log.interceptor.ts +9 -3
  725. package/src/access-log/access-log.service.spec.ts +33 -2
  726. package/src/access-log/access-log.service.ts +38 -0
  727. package/src/access-log/dto/query-access-log.dto.ts +5 -0
  728. package/src/ai/ai-attachment.fragments.spec.ts +319 -0
  729. package/src/ai/ai-attachment.fragments.ts +35 -0
  730. package/src/ai/ai.controller.ts +9 -0
  731. package/src/ai/ai.module.ts +3 -2
  732. package/src/ai/ai.prompts.ts +38 -0
  733. package/src/ai/ai.service.spec.ts +280 -0
  734. package/src/ai/ai.service.ts +475 -82
  735. package/src/ai/dto/chat.dto.ts +33 -3
  736. package/src/ai/dto/create-agent.dto.ts +2 -2
  737. package/src/ai/dto/richtext-edit-html.dto.ts +11 -0
  738. package/src/ai/dto/update-agent.dto.ts +2 -2
  739. package/src/ai/richtext-ai.prompts.ts +57 -0
  740. package/src/ai/richtext-ai.service.ts +109 -0
  741. package/src/ai-instruction/ai-instruction.controller.ts +40 -9
  742. package/src/ai-instruction/ai-instruction.module.ts +13 -2
  743. package/src/ai-instruction/ai-instruction.service.spec.ts +658 -0
  744. package/src/ai-instruction/ai-instruction.service.ts +661 -62
  745. package/src/ai-instruction/ai-prompt-builder.service.ts +10 -1
  746. package/src/ai-instruction/ai-prompt-registry.ts +87 -0
  747. package/src/ai-instruction/ai-prompt-resolver.service.spec.ts +375 -0
  748. package/src/ai-instruction/ai-prompt-resolver.service.ts +295 -0
  749. package/src/ai-instruction/ai-prompt-template.util.spec.ts +162 -0
  750. package/src/ai-instruction/ai-prompt-template.util.ts +199 -0
  751. package/src/ai-instruction/dto/create-ai-instruction.dto.ts +39 -3
  752. package/src/ai-instruction/dto/preview-ai-instruction.dto.ts +26 -0
  753. package/src/ai-instruction/dto/update-ai-instruction.dto.ts +42 -1
  754. package/src/ai-instruction/types/ai-instruction.types.ts +24 -1
  755. package/src/auth/auth.controller.ts +78 -13
  756. package/src/auth/auth.module.ts +2 -0
  757. package/src/auth/auth.service.spec.ts +386 -7
  758. package/src/auth/auth.service.ts +435 -81
  759. package/src/auth/dto/confirm-email.dto.ts +7 -0
  760. package/src/auth/dto/resend-confirmation.dto.ts +20 -0
  761. package/src/authorize/authorize.controller.ts +2 -0
  762. package/src/authorize/authorize.service.spec.ts +23 -0
  763. package/src/authorize/authorize.service.ts +8 -0
  764. package/src/captcha/captcha.module.ts +10 -0
  765. package/src/captcha/captcha.service.spec.ts +210 -0
  766. package/src/captcha/captcha.service.ts +191 -0
  767. package/src/challenge/challenge.service.spec.ts +112 -4
  768. package/src/challenge/challenge.service.ts +131 -17
  769. package/src/core.module.ts +46 -1
  770. package/src/file/file.controller.ts +54 -4
  771. package/src/file/file.service.spec.ts +275 -0
  772. package/src/file/file.service.ts +468 -16
  773. package/src/file/image-optimization.service.spec.ts +92 -0
  774. package/src/file/image-optimization.service.ts +202 -0
  775. package/src/file/provider/abstract.provider.ts +23 -0
  776. package/src/file/provider/gcs.provider.ts +17 -0
  777. package/src/file/provider/local.provider.ts +21 -0
  778. package/src/file/provider/s3.provider.ts +25 -0
  779. package/src/file/provider/spaces.provider.ts +26 -0
  780. package/src/impersonation/dto/redeem-impersonation.dto.ts +8 -0
  781. package/src/impersonation/dto/start-impersonation.dto.ts +13 -0
  782. package/src/impersonation/impersonation-restrictions.ts +84 -0
  783. package/src/impersonation/impersonation.controller.ts +109 -0
  784. package/src/impersonation/impersonation.module.ts +31 -0
  785. package/src/impersonation/impersonation.service.spec.ts +427 -0
  786. package/src/impersonation/impersonation.service.ts +577 -0
  787. package/src/incident/incident-notifier.service.spec.ts +205 -0
  788. package/src/incident/incident-notifier.service.ts +241 -0
  789. package/src/incident/incident.controller.ts +38 -0
  790. package/src/incident/incident.module.ts +19 -0
  791. package/src/incident/incident.service.spec.ts +150 -0
  792. package/src/incident/incident.service.ts +209 -0
  793. package/src/index.ts +58 -0
  794. package/src/integration/services/outbox-polling.coordinator.spec.ts +90 -0
  795. package/src/integration/services/outbox-polling.coordinator.ts +62 -40
  796. package/src/integration/services/outbox.service.spec.ts +8 -0
  797. package/src/integration/services/outbox.service.ts +19 -13
  798. package/src/integration-profile/dto/test.dto.ts +19 -0
  799. package/src/integration-profile/integration-profile.controller.ts +10 -1
  800. package/src/integration-profile/integration-profile.module.spec.ts +51 -0
  801. package/src/integration-profile/integration-profile.module.ts +23 -4
  802. package/src/integration-profile/integration-profile.secrets.ts +12 -1
  803. package/src/integration-profile/integration-profile.service.spec.ts +279 -0
  804. package/src/integration-profile/integration-profile.service.ts +120 -124
  805. package/src/integration-profile/integration-profile.utils.spec.ts +22 -0
  806. package/src/integration-profile/integration-profile.utils.ts +78 -3
  807. package/src/integration-profile/testers/ai.testers.ts +165 -0
  808. package/src/integration-profile/testers/captcha.testers.spec.ts +92 -0
  809. package/src/integration-profile/testers/captcha.testers.ts +139 -0
  810. package/src/integration-profile/testers/email.testers.ts +194 -0
  811. package/src/integration-profile/testers/http-probe.spec.ts +153 -0
  812. package/src/integration-profile/testers/http-probe.ts +171 -0
  813. package/src/integration-profile/testers/index.ts +30 -0
  814. package/src/integration-profile/testers/infrastructure.testers.ts +122 -0
  815. package/src/integration-profile/testers/integration-tester.registry.spec.ts +75 -0
  816. package/src/integration-profile/testers/integration-tester.registry.ts +43 -0
  817. package/src/integration-profile/testers/integration-tester.types.ts +61 -0
  818. package/src/integration-profile/testers/messaging.testers.ts +102 -0
  819. package/src/integration-profile/testers/oauth.testers.spec.ts +220 -0
  820. package/src/integration-profile/testers/oauth.testers.ts +305 -0
  821. package/src/integration-profile/testers/payment.testers.ts +185 -0
  822. package/src/integration-profile/testers/remaining.testers.spec.ts +401 -0
  823. package/src/integration-profile/testers/storage.testers.spec.ts +185 -0
  824. package/src/integration-profile/testers/storage.testers.ts +226 -0
  825. package/src/integration-profile/testers/tester-coverage.spec.ts +58 -0
  826. package/src/language/en.json +15 -2
  827. package/src/language/pt.json +15 -2
  828. package/src/leaked-password/README.md +83 -0
  829. package/src/leaked-password/leaked-password.controller.ts +52 -0
  830. package/src/leaked-password/leaked-password.module.ts +18 -0
  831. package/src/leaked-password/leaked-password.service.ts +242 -0
  832. package/src/leaked-password/seed/build-seed-sql.mjs +180 -0
  833. package/src/leaked-password/seed/common-passwords.txt +335 -0
  834. package/src/mail/dto/preview.dto.ts +50 -0
  835. package/src/mail/dto/send.dto.ts +58 -1
  836. package/src/mail/mail-template.spec.ts +274 -0
  837. package/src/mail/mail.controller.ts +9 -2
  838. package/src/mail/mail.module.ts +2 -0
  839. package/src/mail/mail.service.spec.ts +422 -6
  840. package/src/mail/mail.service.ts +337 -34
  841. package/src/mail-delivery/mail-delivery-provision.service.ts +66 -0
  842. package/src/mail-delivery/mail-delivery.constants.ts +31 -0
  843. package/src/mail-delivery/mail-delivery.controller.ts +38 -0
  844. package/src/mail-delivery/mail-delivery.module.ts +23 -0
  845. package/src/mail-delivery/mail-delivery.service.spec.ts +136 -0
  846. package/src/mail-delivery/mail-delivery.service.ts +152 -0
  847. package/src/mail-delivery/ses-event.parser.spec.ts +140 -0
  848. package/src/mail-delivery/ses-event.parser.ts +191 -0
  849. package/src/mail-delivery/ses-webhook.service.spec.ts +170 -0
  850. package/src/mail-delivery/ses-webhook.service.ts +240 -0
  851. package/src/mail-delivery/sns-message.util.spec.ts +163 -0
  852. package/src/mail-delivery/sns-message.util.ts +125 -0
  853. package/src/mail-layout/dto/update.dto.ts +19 -0
  854. package/src/mail-layout/mail-layout.controller.ts +47 -0
  855. package/src/mail-layout/mail-layout.module.ts +22 -0
  856. package/src/mail-layout/mail-layout.service.ts +94 -0
  857. package/src/mail-sent/dto/query-mail-sent-list.dto.ts +15 -0
  858. package/src/mail-sent/mail-sent.service.ts +46 -2
  859. package/src/mailbox/mailbox-fetcher.registry.ts +132 -0
  860. package/src/mailbox/mailbox.module.ts +14 -0
  861. package/src/mcp-chat/mcp-chat.service.spec.ts +236 -0
  862. package/src/mcp-chat/mcp-chat.service.ts +122 -26
  863. package/src/meeting/meeting-provider.registry.ts +193 -0
  864. package/src/meeting/meeting.constants.ts +53 -0
  865. package/src/meeting/meeting.module.ts +13 -0
  866. package/src/middlewares/locale-injection.middleware.ts +25 -6
  867. package/src/oauth/oauth-token-broker.service.spec.ts +172 -0
  868. package/src/oauth/oauth-token-broker.service.ts +363 -0
  869. package/src/oauth/oauth.module.ts +3 -1
  870. package/src/oauth/oauth.service.ts +8 -2
  871. package/src/oauth/providers/google.provider.ts +49 -15
  872. package/src/observability/monitored-cron.spec.ts +102 -0
  873. package/src/observability/monitored-cron.ts +110 -0
  874. package/src/pairing/dto/approve-reverse-pairing.dto.ts +20 -0
  875. package/src/pairing/dto/claim-pairing.dto.ts +19 -0
  876. package/src/pairing/dto/confirm-pairing.dto.ts +14 -0
  877. package/src/pairing/dto/poll-reverse-pairing.dto.ts +9 -0
  878. package/src/pairing/dto/start-pairing.dto.ts +19 -0
  879. package/src/pairing/dto/start-reverse-pairing.dto.ts +45 -0
  880. package/src/pairing/pairing.controller.ts +246 -0
  881. package/src/pairing/pairing.module.ts +27 -0
  882. package/src/pairing/pairing.service.spec.ts +462 -0
  883. package/src/pairing/pairing.service.ts +614 -0
  884. package/src/pdf/pdf.service.ts +81 -24
  885. package/src/presence/presence.controller.ts +29 -0
  886. package/src/presence/presence.listener.ts +53 -0
  887. package/src/presence/presence.module.ts +19 -0
  888. package/src/presence/presence.service.spec.ts +97 -0
  889. package/src/presence/presence.service.ts +160 -0
  890. package/src/profile/dto/delete-account.dto.ts +11 -0
  891. package/src/profile/events/profile-account-deleted.event.ts +13 -0
  892. package/src/profile/profile.controller.ts +46 -34
  893. package/src/profile/profile.service.ts +164 -46
  894. package/src/realtime/realtime.gateway.ts +138 -0
  895. package/src/realtime/realtime.module.ts +16 -0
  896. package/src/realtime/realtime.service.spec.ts +50 -0
  897. package/src/realtime/realtime.service.ts +187 -0
  898. package/src/redis/redis-pubsub.ts +190 -0
  899. package/src/role/guards/role.guard.spec.ts +75 -0
  900. package/src/role/guards/role.guard.ts +64 -46
  901. package/src/session/session.service.spec.ts +81 -0
  902. package/src/session/session.service.ts +93 -6
  903. package/src/setting/brand-asset.service.ts +137 -0
  904. package/src/setting/dto/create.dto.ts +13 -2
  905. package/src/setting/setting.module.ts +3 -2
  906. package/src/setting/setting.service.spec.ts +135 -1
  907. package/src/setting/setting.service.ts +138 -2
  908. package/src/token/token.service.spec.ts +62 -0
  909. package/src/token/token.service.ts +57 -3
  910. package/src/user/dto/create-with-email-and-password.dto.ts +6 -1
  911. package/src/user/dto/query-user-mail-list.dto.ts +10 -0
  912. package/src/user/user-credential.util.ts +35 -0
  913. package/src/user/user.controller.ts +17 -2
  914. package/src/user/user.service.spec.ts +186 -2
  915. package/src/user/user.service.ts +361 -16
  916. package/src/user-device/user-device.controller.ts +77 -0
  917. package/src/user-device/user-device.module.ts +13 -0
  918. package/src/user-device/user-device.service.spec.ts +234 -0
  919. package/src/user-device/user-device.service.ts +168 -0
  920. package/src/utils/device-key.spec.ts +57 -0
  921. package/src/utils/device-key.ts +32 -0
  922. package/src/utils/geo-ip.spec.ts +134 -0
  923. package/src/utils/geo-ip.ts +93 -0
  924. package/src/utils/locale-context.spec.ts +27 -1
  925. package/src/utils/locale-context.ts +15 -12
  926. package/src/utils/security-event-vars.spec.ts +161 -0
  927. package/src/utils/security-event-vars.ts +153 -0
  928. package/src/utils/user-agent.spec.ts +37 -0
  929. package/src/utils/user-agent.ts +46 -0
  930. package/src/validators/is-strong-password-with-settings.validator.ts +12 -0
  931. package/src/validators/service-locator.ts +15 -0
  932. package/src/webhook-integration/webhook-integration.service.ts +57 -3
  933. package/src/webhook-integration/webhook-protocol.registry.ts +6 -0
  934. package/src/whatsapp-sender/whatsapp-sender.module.ts +10 -0
  935. package/src/whatsapp-sender/whatsapp-sender.service.ts +313 -0
@@ -19,6 +19,7 @@ import { Switch } from '@/components/ui/switch';
19
19
  import { TagsInput } from '@/components/ui/tags-input';
20
20
  import { ByteSizeInput } from './byte-size-input';
21
21
  import { DurationInput } from './duration-input';
22
+ import { buildAbsoluteImageUrl } from '@/lib/build-image-url';
22
23
  import { revalidateSystemIcon } from '@/lib/revalidate-icon';
23
24
  import { cn } from '@/lib/utils';
24
25
  import {
@@ -27,7 +28,8 @@ import {
27
28
  SettingTypeEnum,
28
29
  } from '@hed-hog/api-types';
29
30
  import { useApp } from '@hed-hog/next-app-provider';
30
- import { Eye, EyeOff, Pencil, Plus, Upload } from 'lucide-react';
31
+ import { ExternalLink, Eye, EyeOff, Pencil, Plus, Upload } from 'lucide-react';
32
+ import Link from 'next/link';
31
33
  import { useTranslations } from 'next-intl';
32
34
  import { useEffect, useRef, useState } from 'react';
33
35
 
@@ -42,21 +44,6 @@ type ApiPaginated<T> = {
42
44
  lastPage: number;
43
45
  };
44
46
 
45
- type IntegrationTypeOption = {
46
- id: number;
47
- slug: string;
48
- };
49
-
50
- type IntegrationProviderOption = {
51
- id: number;
52
- slug: string;
53
- type_id: number;
54
- integration_provider_locale?: {
55
- name: string;
56
- locale: { code: string };
57
- }[];
58
- };
59
-
60
47
  type IntegrationProfileOption = {
61
48
  id: number;
62
49
  slug: string;
@@ -90,110 +77,243 @@ function getLocalizedIntegrationName(
90
77
  );
91
78
  }
92
79
 
93
- const MAIL_INTEGRATION_PROFILE_SETTING_SLUG = 'mail-integration-profile-id';
94
- const SCALING_INFRA_PROFILE_SETTING_SLUG = 'scaling.video.integration_profile_id';
95
- const HEDHOG_K8S_INFRA_PROFILE_SETTING_SLUG =
96
- 'hedhog.k8s_integration_profile_id';
97
- const COMMERCE_PAYMENT_PROFILE_SETTING_SLUG =
98
- 'commerce-default-payment-profile-id';
99
- const DAILY_REPORT_EMAIL_INTEGRATION_SETTING_SLUG =
100
- 'operations.daily-report.email-integration-profile-id';
101
- const DAILY_REPORT_WHATSAPP_INTEGRATION_SETTING_SLUG =
102
- 'operations.daily-report.whatsapp-integration-profile-id';
103
- const AI_PROFILE_SETTING_SLUGS = [
104
- 'ai-gemini-profile-id',
105
- 'ai-openai-profile-id',
106
- 'mcp-ai-profile-id',
107
- ];
108
- const OAUTH_PROFILE_SLUG_TO_PROVIDER: Record<string, string> = {
109
- 'oauth-google-profile-id': 'google-oauth',
110
- 'oauth-facebook-profile-id': 'facebook-oauth',
111
- 'oauth-github-profile-id': 'github-oauth',
112
- 'oauth-microsoft-profile-id': 'microsoft-oauth',
113
- 'oauth-microsoft-entra-id-profile-id': 'microsoft-entra-id-oauth',
114
- 'oauth-apple-profile-id': 'apple-oauth',
115
- 'oauth-linkedin-profile-id': 'linkedin-oauth',
80
+ type IntegrationProfileSettingConfig = {
81
+ /**
82
+ * Tipo de perfil aceito pela setting. Filtra a listagem do picker e trava o
83
+ * sheet de criação. Ausente só na rede de segurança, onde listamos tudo.
84
+ */
85
+ typeSlug?: string;
86
+ /** Estreita para um provider específico (OAuth, Kubernetes). */
87
+ providerSlug?: string;
88
+ /** Providers a esconder mesmo sendo do tipo certo. */
89
+ excludeProviders?: string[];
90
+ /** Substantivo usado nos rótulos dos botões editar/criar. */
91
+ profileNameKey?: string;
116
92
  };
117
- // Settings cujo valor é o SLUG de um integration_profile. O picker continua sendo
118
- // selecionado/exibido pelo nome; apenas o valor persistido é o slug.
119
- const ALL_INTEGRATION_PROFILE_SETTING_SLUGS = [
120
- MAIL_INTEGRATION_PROFILE_SETTING_SLUG,
121
- SCALING_INFRA_PROFILE_SETTING_SLUG,
122
- HEDHOG_K8S_INFRA_PROFILE_SETTING_SLUG,
123
- COMMERCE_PAYMENT_PROFILE_SETTING_SLUG,
124
- DAILY_REPORT_EMAIL_INTEGRATION_SETTING_SLUG,
125
- DAILY_REPORT_WHATSAPP_INTEGRATION_SETTING_SLUG,
126
- 'file-storage-profile-id',
127
- 'lms-bulk-upload-storage-profile-id',
128
- ...AI_PROFILE_SETTING_SLUGS,
129
- ...Object.keys(OAUTH_PROFILE_SLUG_TO_PROVIDER),
130
- ];
131
-
132
- function toSlug(value: string) {
133
- return (value || 'profile')
134
- .toLowerCase()
135
- .normalize('NFD')
136
- .replace(/[\u0300-\u036f]/g, '')
137
- .replace(/[^a-z0-9]+/g, '-')
138
- .replace(/^-+|-+$/g, '');
139
- }
140
93
 
141
- function getDailyReportIntegrationTypeSlug(settingSlug: string) {
142
- if (settingSlug === DAILY_REPORT_EMAIL_INTEGRATION_SETTING_SLUG) {
143
- return 'email';
144
- }
94
+ /**
95
+ * Settings cujo valor é o SLUG de um integration_profile — o sufixo `-id` é
96
+ * histórico, nenhuma delas guarda o id numérico. Uma entrada aqui é tudo que
97
+ * uma setting nova precisa para ganhar o picker completo; sem ela o campo cai
98
+ * na rede de segurança de `getIntegrationProfileSettingConfig` (picker sem
99
+ * filtro de tipo).
100
+ *
101
+ * Deve espelhar `SELECT slug FROM setting WHERE component = 'entity-picker'`.
102
+ */
103
+ const INTEGRATION_PROFILE_SETTINGS: Record<
104
+ string,
105
+ IntegrationProfileSettingConfig
106
+ > = {
107
+ // IA
108
+ 'ai-openai-profile-id': { typeSlug: 'ai' },
109
+ 'ai-gemini-profile-id': { typeSlug: 'ai' },
110
+ 'ai-deepseek-profile-id': { typeSlug: 'ai' },
111
+ 'mcp-ai-profile-id': { typeSlug: 'ai' },
112
+ 'richtext-ai-profile-id': { typeSlug: 'ai' },
113
+ 'lms-ai-course-generation-profile-id': { typeSlug: 'ai' },
114
+ 'lms-ai-lesson-xp-profile-id': { typeSlug: 'ai' },
115
+ 'lms-ai-transcription-profile-id': { typeSlug: 'ai' },
116
+ 'lms-ai-course-image-profile-id': { typeSlug: 'ai' },
117
+ 'lms-ai-discussion-reply-profile-id': { typeSlug: 'ai' },
118
+ 'sac.ai.integration-profile-id': { typeSlug: 'ai' },
119
+ // Transcrição de áudio só existe na OpenAI; oferecer Gemini ou Claude aqui
120
+ // deixaria escolher um perfil que o worker recusa.
121
+ 'sac.whatsapp.transcription-profile-id': {
122
+ typeSlug: 'ai',
123
+ providerSlug: 'openai',
124
+ },
125
+ 'campaign.ai.integration-profile-id': { typeSlug: 'ai' },
126
+
127
+ // E-mail. O IMAP é do tipo `email` mas só serve para receber:
128
+ // `buildMailConfigFromIntegration` recusa usá-lo no envio, então oferecê-lo
129
+ // aqui seria deixar escolher um perfil que o backend rejeita depois.
130
+ 'mail-integration-profile-id': {
131
+ typeSlug: 'email',
132
+ excludeProviders: ['imap'],
133
+ },
134
+ 'lms-discussion-notify-email-profile-id': {
135
+ typeSlug: 'email',
136
+ excludeProviders: ['imap'],
137
+ },
138
+ 'lms-digest-notify-email-profile-id': {
139
+ typeSlug: 'email',
140
+ excludeProviders: ['imap'],
141
+ },
142
+ 'lms-celebration-notify-email-profile-id': {
143
+ typeSlug: 'email',
144
+ excludeProviders: ['imap'],
145
+ },
146
+ 'lms-class-reminder-notify-email-profile-id': {
147
+ typeSlug: 'email',
148
+ excludeProviders: ['imap'],
149
+ },
150
+ 'operations.daily-report.email-integration-profile-id': {
151
+ typeSlug: 'email',
152
+ excludeProviders: ['imap'],
153
+ },
154
+ 'operations.approval-notifications.email-integration-profile-id': {
155
+ typeSlug: 'email',
156
+ excludeProviders: ['imap'],
157
+ },
158
+
159
+ // WhatsApp
160
+ 'lms-discussion-notify-whatsapp-profile-id': { typeSlug: 'whatsapp' },
161
+ 'lms-digest-notify-whatsapp-profile-id': { typeSlug: 'whatsapp' },
162
+ 'lms-celebration-notify-whatsapp-profile-id': { typeSlug: 'whatsapp' },
163
+ 'lms-class-reminder-notify-whatsapp-profile-id': { typeSlug: 'whatsapp' },
164
+ 'operations.daily-report.whatsapp-integration-profile-id': {
165
+ typeSlug: 'whatsapp',
166
+ },
167
+ 'operations.approval-notifications.whatsapp-integration-profile-id': {
168
+ typeSlug: 'whatsapp',
169
+ },
170
+ 'sac.notify-whatsapp-profile-id': { typeSlug: 'whatsapp' },
171
+
172
+ // Armazenamento
173
+ 'file-storage-profile-id': { typeSlug: 'storage' },
174
+ 'lms-bulk-upload-storage-profile-id': { typeSlug: 'storage' },
175
+
176
+ // OAuth — um provider por setting
177
+ 'oauth-google-profile-id': {
178
+ typeSlug: 'oauth',
179
+ providerSlug: 'google-oauth',
180
+ },
181
+ 'oauth-facebook-profile-id': {
182
+ typeSlug: 'oauth',
183
+ providerSlug: 'facebook-oauth',
184
+ },
185
+ 'oauth-github-profile-id': {
186
+ typeSlug: 'oauth',
187
+ providerSlug: 'github-oauth',
188
+ },
189
+ 'oauth-microsoft-profile-id': {
190
+ typeSlug: 'oauth',
191
+ providerSlug: 'microsoft-oauth',
192
+ },
193
+ 'oauth-microsoft-entra-id-profile-id': {
194
+ typeSlug: 'oauth',
195
+ providerSlug: 'microsoft-entra-id-oauth',
196
+ },
197
+ 'oauth-apple-profile-id': { typeSlug: 'oauth', providerSlug: 'apple-oauth' },
198
+ 'oauth-linkedin-profile-id': {
199
+ typeSlug: 'oauth',
200
+ providerSlug: 'linkedin-oauth',
201
+ },
202
+
203
+ // Pagamento. O Google Play só cobra por compra dentro do app Android
204
+ // (`commerce-android-checkout-mode`), então escolhê-lo como gateway padrão
205
+ // quebraria o checkout do site. O backend também recusa — isto é só para não
206
+ // oferecer.
207
+ 'commerce-default-payment-profile-id': {
208
+ typeSlug: 'payment',
209
+ excludeProviders: ['google_play'],
210
+ },
211
+
212
+ // Infraestrutura
213
+ 'scaling.video.integration_profile_id': { typeSlug: 'infrastructure' },
214
+ 'hedhog.k8s_integration_profile_id': {
215
+ typeSlug: 'infrastructure',
216
+ providerSlug: 'kubernetes',
217
+ profileNameKey: 'profileTypeNames.kubernetes',
218
+ },
219
+ };
145
220
 
146
- if (settingSlug === DAILY_REPORT_WHATSAPP_INTEGRATION_SETTING_SLUG) {
147
- return 'whatsapp';
148
- }
221
+ /** Chaves de tradução do picker, por tipo de perfil. */
222
+ const PROFILE_TYPE_TRANSLATION_KEYS: Record<
223
+ string,
224
+ { placeholder: string; emptyState: string; name: string }
225
+ > = {
226
+ ai: {
227
+ placeholder: 'selectAiProfile',
228
+ emptyState: 'aiProfileEmptyState',
229
+ name: 'profileTypeNames.ai',
230
+ },
231
+ email: {
232
+ placeholder: 'selectMailProfile',
233
+ emptyState: 'mailProfileEmptyState',
234
+ name: 'profileTypeNames.email',
235
+ },
236
+ whatsapp: {
237
+ placeholder: 'selectWhatsAppProfile',
238
+ emptyState: 'whatsappProfileEmptyState',
239
+ name: 'profileTypeNames.whatsapp',
240
+ },
241
+ storage: {
242
+ placeholder: 'selectStorageProfile',
243
+ emptyState: 'storageProfileEmptyState',
244
+ name: 'profileTypeNames.storage',
245
+ },
246
+ oauth: {
247
+ placeholder: 'selectOAuthProfile',
248
+ emptyState: 'oauthProfileEmptyState',
249
+ name: 'profileTypeNames.oauth',
250
+ },
251
+ payment: {
252
+ placeholder: 'selectPaymentProfile',
253
+ emptyState: 'paymentProfileEmptyState',
254
+ name: 'profileTypeNames.payment',
255
+ },
256
+ infrastructure: {
257
+ placeholder: 'selectInfraProfile',
258
+ emptyState: 'infraProfileEmptyState',
259
+ name: 'profileTypeNames.infrastructure',
260
+ },
261
+ };
262
+
263
+ const GENERIC_PROFILE_TRANSLATION_KEYS = {
264
+ placeholder: 'selectIntegrationProfile',
265
+ emptyState: 'integrationProfileEmptyState',
266
+ name: 'profileTypeNames.generic',
267
+ };
268
+
269
+ /**
270
+ * Convenção de nome das settings que guardam um perfil: `...-profile-id`,
271
+ * `...integration_profile_id` e variantes.
272
+ */
273
+ const PROFILE_SETTING_SLUG_PATTERN =
274
+ /(^|[.\-_])(integration[-_])?profile[-_]id$/;
275
+
276
+ const warnedUnmappedProfileSettings = new Set<string>();
149
277
 
150
- return null;
278
+ /** Settings cujo valor exibido precisa ser resolvido a partir do slug salvo. */
279
+ function isIntegrationProfileSetting(settingSlug: string) {
280
+ return (
281
+ settingSlug in INTEGRATION_PROFILE_SETTINGS ||
282
+ PROFILE_SETTING_SLUG_PATTERN.test(settingSlug)
283
+ );
151
284
  }
152
285
 
153
- function buildIntegrationProfileConfig(
154
- providerSlug: string,
155
- values: Record<string, string>
156
- ) {
157
- const trim = (key: string) => String(values[key] ?? '').trim();
158
-
159
- if (providerSlug === 'smtp') {
160
- return {
161
- host: trim('host'),
162
- port: Number(trim('port') || 587),
163
- username: trim('username'),
164
- password: trim('password'),
165
- secure: values.secure === 'true',
166
- from_email: trim('from_email'),
167
- from_name: trim('from_name'),
168
- reply_to_email: trim('reply_to_email'),
169
- };
286
+ /**
287
+ * Config do picker para uma setting `entity-picker`. Slug fora do registry mas
288
+ * com cara de campo de perfil ainda ganha um picker — sem filtro de tipo, mas
289
+ * melhor do que obrigar o administrador a digitar o slug à mão (era o que
290
+ * acontecia com `sac.ai.integration-profile-id`). `null` = não é campo de
291
+ * perfil, renderiza como texto.
292
+ */
293
+ function getIntegrationProfileSettingConfig(
294
+ settingSlug: string
295
+ ): IntegrationProfileSettingConfig | null {
296
+ const config = INTEGRATION_PROFILE_SETTINGS[settingSlug];
297
+ if (config) {
298
+ return config;
170
299
  }
171
300
 
172
- if (providerSlug === 'gmail') {
173
- return {
174
- client_id: trim('client_id'),
175
- client_secret: trim('client_secret'),
176
- refresh_token: trim('refresh_token'),
177
- from_email: trim('from_email'),
178
- from_name: trim('from_name'),
179
- };
301
+ if (!PROFILE_SETTING_SLUG_PATTERN.test(settingSlug)) {
302
+ return null;
180
303
  }
181
304
 
182
- if (providerSlug === 'ses') {
183
- return {
184
- access_key_id: trim('access_key_id'),
185
- secret_access_key: trim('secret_access_key'),
186
- region: trim('region'),
187
- from_email: trim('from_email'),
188
- from_name: trim('from_name'),
189
- };
305
+ if (
306
+ process.env.NODE_ENV !== 'production' &&
307
+ !warnedUnmappedProfileSettings.has(settingSlug)
308
+ ) {
309
+ warnedUnmappedProfileSettings.add(settingSlug);
310
+ console.warn(
311
+ `[setting-field] "${settingSlug}" não está em INTEGRATION_PROFILE_SETTINGS; ` +
312
+ 'listando perfis de integração de todos os tipos.'
313
+ );
190
314
  }
191
315
 
192
- return {
193
- host: trim('host'),
194
- token: trim('token'),
195
- instance_name: trim('instance_name'),
196
- };
316
+ return {};
197
317
  }
198
318
 
199
319
  export const SettingField = ({ setting, className }: SettingFieldProps) => {
@@ -207,6 +327,15 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
207
327
  } = useApp();
208
328
  const component =
209
329
  setting.component?.replaceAll('_', '-') || SettingComponentEnum.INPUT_TEXT;
330
+ /**
331
+ * Rótulo de uma opção de lista (radio/combobox). Valores como `play_billing`
332
+ * não dizem nada ao administrador, então procuramos uma tradução dedicada em
333
+ * `settingOptions.<slug>.<valor>` e só caímos no valor cru quando não existe.
334
+ */
335
+ const optionLabel = (value: string) => {
336
+ const key = `settingOptions.${setting.slug}.${value}`;
337
+ return t.has(key) ? t(key) : value;
338
+ };
210
339
  const [localValue, setLocalValue] = useState(setting.value);
211
340
  const [storageProfileSelectedLabel, setStorageProfileSelectedLabel] =
212
341
  useState('');
@@ -282,14 +411,16 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
282
411
  }, [localValue]);
283
412
 
284
413
  useEffect(() => {
285
- if (!ALL_INTEGRATION_PROFILE_SETTING_SLUGS.includes(setting.slug)) {
414
+ if (!isIntegrationProfileSetting(setting.slug)) {
286
415
  setStorageProfileSelectedLabel('');
287
416
  setResolvedProfileId(null);
288
417
  return;
289
418
  }
290
419
 
420
+ // `'0'` é o default herdado das settings antigas que nomeavam o campo como
421
+ // "profile id": não é slug de perfil nenhum, e buscá-lo devolveria vazio.
291
422
  const profileSlug = String(localValue ?? '').trim();
292
- if (!profileSlug) {
423
+ if (!profileSlug || profileSlug === '0') {
293
424
  setStorageProfileSelectedLabel('');
294
425
  setResolvedProfileId(null);
295
426
  return;
@@ -485,28 +616,24 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
485
616
  },
486
617
  });
487
618
 
488
- const fileId = response.data?.id;
619
+ const fileId = Number(response.data?.id);
489
620
 
490
621
  if (!fileId) {
491
622
  throw new Error(t('fileIdNotReturned'));
492
623
  }
493
624
 
494
- const openResponse = await request<{ url?: string }>({
495
- url: `/file/open/${fileId}`,
496
- method: 'PUT',
497
- });
498
-
499
- const openUrl = openResponse?.data?.url;
625
+ // `/file/image/:id` é público, imutável (Cache-Control + ETag) e não
626
+ // expira. A URL de `PUT /file/open/:id`, gravada aqui antes, carrega
627
+ // um JWT de 3600s: a setting virava link morto uma hora depois do
628
+ // upload. Absoluta de propósito — o mesmo valor é lido pelo <img> da
629
+ // sidebar, pelo favicon dinâmico, pelo metadata do layout e pelo
630
+ // e-mail transacional, que roda fora do browser.
631
+ const fileUrl = buildAbsoluteImageUrl(fileId);
500
632
 
501
- if (!openUrl) {
502
- throw new Error(t('temporaryOpenUrlNotReturned'));
633
+ if (!fileUrl) {
634
+ throw new Error(t('apiBaseUrlNotConfigured'));
503
635
  }
504
636
 
505
- const fileUrl =
506
- typeof openUrl === 'string' && openUrl.startsWith('http')
507
- ? openUrl
508
- : `${String(process.env.NEXT_PUBLIC_API_BASE_URL || '')}${openUrl}`;
509
-
510
637
  handleChangeValue(formatValue(fileUrl));
511
638
  showToastHandler('success', t('fileUploaded'));
512
639
  } catch (error) {
@@ -649,7 +776,7 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
649
776
  <SelectContent>
650
777
  {options.map((option) => (
651
778
  <SelectItem key={option.id} value={option.value}>
652
- {option.value}
779
+ {optionLabel(option.value)}
653
780
  </SelectItem>
654
781
  ))}
655
782
  </SelectContent>
@@ -671,7 +798,9 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
671
798
  id={`radio-${option.id}`}
672
799
  className="bg-background"
673
800
  />
674
- <Label htmlFor={`radio-${option.id}`}>{option.value}</Label>
801
+ <Label htmlFor={`radio-${option.id}`}>
802
+ {optionLabel(option.value)}
803
+ </Label>
675
804
  </div>
676
805
  ))}
677
806
  </RadioGroup>
@@ -702,781 +831,30 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
702
831
  </div>
703
832
  );
704
833
 
705
- case SettingComponentEnum.ENTITY_PICKER:
706
- const dailyReportIntegrationTypeSlug = getDailyReportIntegrationTypeSlug(
707
- setting.slug
708
- );
709
-
710
- if (
711
- setting.slug === 'file-storage-profile-id' ||
712
- setting.slug === 'lms-bulk-upload-storage-profile-id'
713
- ) {
714
- const loadStorageTypeId = async () => {
715
- const typesResponse = await request<IntegrationTypeOption[]>({
716
- url: '/integration-profile/types',
717
- method: 'GET',
718
- });
719
- const types = (typesResponse.data ?? []) as IntegrationTypeOption[];
720
- const typeId = types.find((type) => type.slug === 'storage')?.id;
721
-
722
- if (!typeId) {
723
- throw new Error('Integration type "storage" not found.');
724
- }
834
+ // Read-only navigation shortcut: takes the user from a module's settings tab
835
+ // to a related screen. Deliberately does NOT set isTextInputRef nor call
836
+ // handleChangeValue — rendering it must never write anything.
837
+ case SettingComponentEnum.LINK: {
838
+ const href = String(parsedValue || '');
839
+ // Internal paths only. A setting value must not become an open redirect.
840
+ const safeHref =
841
+ href.startsWith('/') && !href.startsWith('//') ? href : '#';
725
842
 
726
- return typeId;
727
- };
728
-
729
- return (
730
- <>
731
- <div className="flex w-full min-w-0 items-center gap-2 md:max-w-72">
732
- <EntityPicker<IntegrationProfileOption & Record<string, unknown>>
733
- placeholder={t('selectStorageProfile')}
734
- emptyStateDescription={t('storageProfileEmptyState')}
735
- value={parsedValue || null}
736
- onChange={(slug, option) => {
737
- setStorageProfileSelectedLabel(option?.name ?? '');
738
- setResolvedProfileId(
739
- option ? Number((option as { id?: number }).id) || null : null
740
- );
741
- handleChangeValue(slug != null ? String(slug) : '', true);
742
- }}
743
- valueType="string"
744
- initialSelectedLabel={storageProfileSelectedLabel}
745
- showCreateButton={false}
746
- clearable={false}
747
- loadOptions={async ({ page, pageSize, search }) => {
748
- const typeId = await loadStorageTypeId();
749
- const qs = new URLSearchParams({
750
- page: String(page),
751
- pageSize: String(pageSize),
752
- typeId: String(typeId),
753
- });
754
- if (search) qs.set('search', search);
755
- const response = await request<
756
- ApiPaginated<
757
- IntegrationProfileOption & Record<string, unknown>
758
- >
759
- >({
760
- url: `/integration-profile?${qs}`,
761
- method: 'GET',
762
- });
763
- const paged = response.data;
764
- return {
765
- items: paged?.data ?? [],
766
- hasMore: (paged?.page ?? 0) < (paged?.lastPage ?? 0),
767
- };
768
- }}
769
- getOptionValue={(opt) => opt.slug}
770
- getOptionLabel={(opt) => String(opt.name ?? '')}
771
- getOptionDescription={(opt) => {
772
- const providerSlug = String(
773
- opt.integration_provider?.slug ?? ''
774
- );
775
- const providerName = getLocalizedIntegrationName(
776
- opt.integration_provider?.integration_provider_locale,
777
- currentLocaleCode
778
- );
779
- return [providerName || providerSlug, opt.slug]
780
- .filter(Boolean)
781
- .join(' • ');
782
- }}
783
- className={cn('flex-1 min-w-0', className)}
784
- />
785
-
786
- <Button
787
- type="button"
788
- variant="outline"
789
- size="icon"
790
- className="h-10 w-10 shrink-0"
791
- onClick={() => {
792
- setStorageProfileEditingId(resolvedProfileId);
793
- setIsStorageProfileSheetOpen(true);
794
- }}
795
- disabled={!resolvedProfileId}
796
- aria-label="Editar perfil de armazenamento"
797
- title="Editar perfil de armazenamento"
798
- >
799
- <Pencil className="h-4 w-4" />
800
- </Button>
801
-
802
- <Button
803
- type="button"
804
- variant="outline"
805
- size="icon"
806
- className="h-10 w-10 shrink-0"
807
- onClick={() => {
808
- setStorageProfileEditingId(null);
809
- setIsStorageProfileSheetOpen(true);
810
- }}
811
- aria-label="Criar perfil de armazenamento"
812
- title="Criar perfil de armazenamento"
813
- >
814
- <Plus className="h-4 w-4" />
815
- </Button>
816
- </div>
817
-
818
- <IntegrationProfileSheet
819
- open={isStorageProfileSheetOpen}
820
- onOpenChange={(nextOpen) => {
821
- setIsStorageProfileSheetOpen(nextOpen);
822
- if (!nextOpen) {
823
- setStorageProfileEditingId(null);
824
- }
825
- }}
826
- profileId={storageProfileEditingId}
827
- initialTypeSlug="storage"
828
- lockedTypeSlug="storage"
829
- onSaved={(profile: IntegrationProfileSheetSavedProfile) => {
830
- setStorageProfileSelectedLabel(profile.name ?? '');
831
- setResolvedProfileId(profile.id ?? null);
832
- handleChangeValue(String(profile.slug ?? ''), true);
833
- }}
834
- />
835
- </>
836
- );
837
- }
838
-
839
- if (AI_PROFILE_SETTING_SLUGS.includes(setting.slug)) {
840
- return (
841
- <>
842
- <div className="flex w-full min-w-0 items-center gap-2 md:max-w-72">
843
- <EntityPicker<IntegrationProfileOption & Record<string, unknown>>
844
- placeholder={t('selectAiProfile')}
845
- emptyStateDescription={t('aiProfileEmptyState')}
846
- value={parsedValue || null}
847
- onChange={(slug, option) => {
848
- setStorageProfileSelectedLabel(option?.name ?? '');
849
- setResolvedProfileId(
850
- option ? Number((option as { id?: number }).id) || null : null
851
- );
852
- handleChangeValue(slug != null ? String(slug) : '', true);
853
- }}
854
- valueType="string"
855
- initialSelectedLabel={storageProfileSelectedLabel}
856
- showCreateButton={false}
857
- clearable={false}
858
- loadOptions={async ({ page, pageSize, search }) => {
859
- const qs = new URLSearchParams({
860
- page: String(page),
861
- pageSize: String(pageSize),
862
- typeSlug: 'ai',
863
- });
864
- if (search) qs.set('search', search);
865
- const response = await request<
866
- ApiPaginated<
867
- IntegrationProfileOption & Record<string, unknown>
868
- >
869
- >({
870
- url: `/integration-profile?${qs}`,
871
- method: 'GET',
872
- });
873
- const paged = response.data;
874
- return {
875
- items: paged?.data ?? [],
876
- hasMore: (paged?.page ?? 0) < (paged?.lastPage ?? 0),
877
- };
878
- }}
879
- getOptionValue={(opt) => opt.slug}
880
- getOptionLabel={(opt) => String(opt.name ?? '')}
881
- getOptionDescription={(opt) => {
882
- const providerSlug = String(
883
- opt.integration_provider?.slug ?? ''
884
- );
885
- const providerName = getLocalizedIntegrationName(
886
- opt.integration_provider?.integration_provider_locale,
887
- currentLocaleCode
888
- );
889
- return [providerName || providerSlug, opt.slug]
890
- .filter(Boolean)
891
- .join(' • ');
892
- }}
893
- className={cn('flex-1 min-w-0', className)}
894
- />
895
-
896
- <Button
897
- type="button"
898
- variant="outline"
899
- size="icon"
900
- className="h-10 w-10 shrink-0"
901
- onClick={() => {
902
- setStorageProfileEditingId(resolvedProfileId);
903
- setIsStorageProfileSheetOpen(true);
904
- }}
905
- disabled={!resolvedProfileId}
906
- aria-label="Editar perfil de IA"
907
- title="Editar perfil de IA"
908
- >
909
- <Pencil className="h-4 w-4" />
910
- </Button>
911
-
912
- <Button
913
- type="button"
914
- variant="outline"
915
- size="icon"
916
- className="h-10 w-10 shrink-0"
917
- onClick={() => {
918
- setStorageProfileEditingId(null);
919
- setIsStorageProfileSheetOpen(true);
920
- }}
921
- aria-label="Criar perfil de IA"
922
- title="Criar perfil de IA"
923
- >
924
- <Plus className="h-4 w-4" />
925
- </Button>
926
- </div>
927
-
928
- <IntegrationProfileSheet
929
- open={isStorageProfileSheetOpen}
930
- onOpenChange={(nextOpen) => {
931
- setIsStorageProfileSheetOpen(nextOpen);
932
- if (!nextOpen) setStorageProfileEditingId(null);
933
- }}
934
- profileId={storageProfileEditingId}
935
- initialTypeSlug="ai"
936
- lockedTypeSlug="ai"
937
- onSaved={(profile: IntegrationProfileSheetSavedProfile) => {
938
- setStorageProfileSelectedLabel(profile.name ?? '');
939
- setResolvedProfileId(profile.id ?? null);
940
- handleChangeValue(String(profile.slug ?? ''), true);
941
- }}
942
- />
943
- </>
944
- );
945
- }
946
-
947
- const oauthProviderSlug = OAUTH_PROFILE_SLUG_TO_PROVIDER[setting.slug];
948
- if (oauthProviderSlug) {
949
- return (
950
- <>
951
- <div className="flex w-full min-w-0 items-center gap-2 md:max-w-72">
952
- <EntityPicker<IntegrationProfileOption & Record<string, unknown>>
953
- placeholder={t('selectOAuthProfile')}
954
- emptyStateDescription={t('oauthProfileEmptyState')}
955
- value={parsedValue || null}
956
- onChange={(slug, option) => {
957
- setStorageProfileSelectedLabel(option?.name ?? '');
958
- setResolvedProfileId(
959
- option ? Number((option as { id?: number }).id) || null : null
960
- );
961
- handleChangeValue(slug != null ? String(slug) : '', true);
962
- }}
963
- valueType="string"
964
- initialSelectedLabel={storageProfileSelectedLabel}
965
- showCreateButton={false}
966
- clearable={false}
967
- loadOptions={async ({ page, pageSize, search }) => {
968
- const qs = new URLSearchParams({
969
- page: String(page),
970
- pageSize: String(pageSize),
971
- providerSlug: oauthProviderSlug,
972
- });
973
- if (search) qs.set('search', search);
974
- const response = await request<
975
- ApiPaginated<
976
- IntegrationProfileOption & Record<string, unknown>
977
- >
978
- >({
979
- url: `/integration-profile?${qs}`,
980
- method: 'GET',
981
- });
982
- const paged = response.data;
983
- return {
984
- items: paged?.data ?? [],
985
- hasMore: (paged?.page ?? 0) < (paged?.lastPage ?? 0),
986
- };
987
- }}
988
- getOptionValue={(opt) => opt.slug}
989
- getOptionLabel={(opt) => String(opt.name ?? '')}
990
- getOptionDescription={(opt) => {
991
- const providerSlug = String(
992
- opt.integration_provider?.slug ?? ''
993
- );
994
- const providerName = getLocalizedIntegrationName(
995
- opt.integration_provider?.integration_provider_locale,
996
- currentLocaleCode
997
- );
998
- return [providerName || providerSlug, opt.slug]
999
- .filter(Boolean)
1000
- .join(' • ');
1001
- }}
1002
- className={cn('flex-1 min-w-0', className)}
1003
- />
1004
-
1005
- <Button
1006
- type="button"
1007
- variant="outline"
1008
- size="icon"
1009
- className="h-10 w-10 shrink-0"
1010
- onClick={() => {
1011
- setStorageProfileEditingId(resolvedProfileId);
1012
- setIsStorageProfileSheetOpen(true);
1013
- }}
1014
- disabled={!resolvedProfileId}
1015
- aria-label="Editar perfil OAuth"
1016
- title="Editar perfil OAuth"
1017
- >
1018
- <Pencil className="h-4 w-4" />
1019
- </Button>
1020
-
1021
- <Button
1022
- type="button"
1023
- variant="outline"
1024
- size="icon"
1025
- className="h-10 w-10 shrink-0"
1026
- onClick={() => {
1027
- setStorageProfileEditingId(null);
1028
- setIsStorageProfileSheetOpen(true);
1029
- }}
1030
- aria-label="Criar perfil OAuth"
1031
- title="Criar perfil OAuth"
1032
- >
1033
- <Plus className="h-4 w-4" />
1034
- </Button>
1035
- </div>
1036
-
1037
- <IntegrationProfileSheet
1038
- open={isStorageProfileSheetOpen}
1039
- onOpenChange={(nextOpen) => {
1040
- setIsStorageProfileSheetOpen(nextOpen);
1041
- if (!nextOpen) setStorageProfileEditingId(null);
1042
- }}
1043
- profileId={storageProfileEditingId}
1044
- initialTypeSlug="oauth"
1045
- lockedTypeSlug="oauth"
1046
- onSaved={(profile: IntegrationProfileSheetSavedProfile) => {
1047
- setStorageProfileSelectedLabel(profile.name ?? '');
1048
- setResolvedProfileId(profile.id ?? null);
1049
- handleChangeValue(String(profile.slug ?? ''), true);
1050
- }}
1051
- />
1052
- </>
1053
- );
1054
- }
1055
-
1056
- if (setting.slug === COMMERCE_PAYMENT_PROFILE_SETTING_SLUG) {
1057
- return (
1058
- <>
1059
- <div className="flex w-full min-w-0 items-center gap-2 md:max-w-72">
1060
- <EntityPicker<IntegrationProfileOption & Record<string, unknown>>
1061
- placeholder={t('selectPaymentProfile')}
1062
- emptyStateDescription={t('paymentProfileEmptyState')}
1063
- value={parsedValue || null}
1064
- onChange={(slug, option) => {
1065
- setStorageProfileSelectedLabel(option?.name ?? '');
1066
- setResolvedProfileId(
1067
- option ? Number((option as { id?: number }).id) || null : null
1068
- );
1069
- handleChangeValue(slug != null ? String(slug) : '', true);
1070
- }}
1071
- valueType="string"
1072
- initialSelectedLabel={storageProfileSelectedLabel}
1073
- showCreateButton={false}
1074
- clearable={false}
1075
- loadOptions={async ({ page, pageSize, search }) => {
1076
- const qs = new URLSearchParams({
1077
- page: String(page),
1078
- pageSize: String(pageSize),
1079
- typeSlug: 'payment',
1080
- });
1081
- if (search) qs.set('search', search);
1082
- const response = await request<
1083
- ApiPaginated<
1084
- IntegrationProfileOption & Record<string, unknown>
1085
- >
1086
- >({
1087
- url: `/integration-profile?${qs}`,
1088
- method: 'GET',
1089
- });
1090
- const paged = response.data;
1091
- return {
1092
- items: paged?.data ?? [],
1093
- hasMore: (paged?.page ?? 0) < (paged?.lastPage ?? 0),
1094
- };
1095
- }}
1096
- getOptionValue={(opt) => opt.slug}
1097
- getOptionLabel={(opt) => String(opt.name ?? '')}
1098
- getOptionDescription={(opt) => {
1099
- const providerSlug = String(
1100
- opt.integration_provider?.slug ?? ''
1101
- );
1102
- const providerName = getLocalizedIntegrationName(
1103
- opt.integration_provider?.integration_provider_locale,
1104
- currentLocaleCode
1105
- );
1106
- return [providerName || providerSlug, opt.slug]
1107
- .filter(Boolean)
1108
- .join(' • ');
1109
- }}
1110
- className={cn('flex-1 min-w-0', className)}
1111
- />
1112
-
1113
- <Button
1114
- type="button"
1115
- variant="outline"
1116
- size="icon"
1117
- className="h-10 w-10 shrink-0"
1118
- onClick={() => {
1119
- setStorageProfileEditingId(resolvedProfileId);
1120
- setIsStorageProfileSheetOpen(true);
1121
- }}
1122
- disabled={!resolvedProfileId}
1123
- aria-label="Editar perfil de pagamento"
1124
- title="Editar perfil de pagamento"
1125
- >
1126
- <Pencil className="h-4 w-4" />
1127
- </Button>
1128
-
1129
- <Button
1130
- type="button"
1131
- variant="outline"
1132
- size="icon"
1133
- className="h-10 w-10 shrink-0"
1134
- onClick={() => {
1135
- setStorageProfileEditingId(null);
1136
- setIsStorageProfileSheetOpen(true);
1137
- }}
1138
- aria-label="Criar perfil de pagamento"
1139
- title="Criar perfil de pagamento"
1140
- >
1141
- <Plus className="h-4 w-4" />
1142
- </Button>
1143
- </div>
1144
-
1145
- <IntegrationProfileSheet
1146
- open={isStorageProfileSheetOpen}
1147
- onOpenChange={(nextOpen) => {
1148
- setIsStorageProfileSheetOpen(nextOpen);
1149
- if (!nextOpen) setStorageProfileEditingId(null);
1150
- }}
1151
- profileId={storageProfileEditingId}
1152
- initialTypeSlug="payment"
1153
- lockedTypeSlug="payment"
1154
- onSaved={(profile: IntegrationProfileSheetSavedProfile) => {
1155
- setStorageProfileSelectedLabel(profile.name ?? '');
1156
- setResolvedProfileId(profile.id ?? null);
1157
- handleChangeValue(String(profile.slug ?? ''), true);
1158
- }}
1159
- />
1160
- </>
1161
- );
1162
- }
1163
-
1164
- if (setting.slug === SCALING_INFRA_PROFILE_SETTING_SLUG) {
1165
- return (
1166
- <>
1167
- <div className="flex w-full min-w-0 items-center gap-2 md:max-w-72">
1168
- <EntityPicker<IntegrationProfileOption & Record<string, unknown>>
1169
- placeholder={t('selectInfraProfile')}
1170
- emptyStateDescription={t('infraProfileEmptyState')}
1171
- value={parsedValue || null}
1172
- onChange={(slug, option) => {
1173
- setStorageProfileSelectedLabel(option?.name ?? '');
1174
- setResolvedProfileId(
1175
- option ? Number((option as { id?: number }).id) || null : null
1176
- );
1177
- handleChangeValue(slug != null ? String(slug) : '', true);
1178
- }}
1179
- valueType="string"
1180
- initialSelectedLabel={storageProfileSelectedLabel}
1181
- showCreateButton={false}
1182
- clearable
1183
- loadOptions={async ({ page, pageSize, search }) => {
1184
- const qs = new URLSearchParams({
1185
- page: String(page),
1186
- pageSize: String(pageSize),
1187
- typeSlug: 'infrastructure',
1188
- });
1189
- if (search) qs.set('search', search);
1190
- const response = await request<
1191
- ApiPaginated<
1192
- IntegrationProfileOption & Record<string, unknown>
1193
- >
1194
- >({
1195
- url: `/integration-profile?${qs}`,
1196
- method: 'GET',
1197
- });
1198
- const paged = response.data;
1199
- return {
1200
- items: paged?.data ?? [],
1201
- hasMore: (paged?.page ?? 0) < (paged?.lastPage ?? 0),
1202
- };
1203
- }}
1204
- getOptionValue={(opt) => opt.slug}
1205
- getOptionLabel={(opt) => String(opt.name ?? '')}
1206
- getOptionDescription={(opt) => {
1207
- const providerSlug = String(
1208
- opt.integration_provider?.slug ?? ''
1209
- );
1210
- const providerName = getLocalizedIntegrationName(
1211
- opt.integration_provider?.integration_provider_locale,
1212
- currentLocaleCode
1213
- );
1214
- return [providerName || providerSlug, opt.slug]
1215
- .filter(Boolean)
1216
- .join(' • ');
1217
- }}
1218
- className={cn('flex-1 min-w-0', className)}
1219
- />
1220
-
1221
- <Button
1222
- type="button"
1223
- variant="outline"
1224
- size="icon"
1225
- className="h-10 w-10 shrink-0"
1226
- onClick={() => {
1227
- setStorageProfileEditingId(resolvedProfileId);
1228
- setIsStorageProfileSheetOpen(true);
1229
- }}
1230
- disabled={!resolvedProfileId}
1231
- aria-label="Editar perfil de infraestrutura"
1232
- title="Editar perfil de infraestrutura"
1233
- >
1234
- <Pencil className="h-4 w-4" />
1235
- </Button>
1236
-
1237
- <Button
1238
- type="button"
1239
- variant="outline"
1240
- size="icon"
1241
- className="h-10 w-10 shrink-0"
1242
- onClick={() => {
1243
- setStorageProfileEditingId(null);
1244
- setIsStorageProfileSheetOpen(true);
1245
- }}
1246
- aria-label="Criar perfil de infraestrutura"
1247
- title="Criar perfil de infraestrutura"
1248
- >
1249
- <Plus className="h-4 w-4" />
1250
- </Button>
1251
- </div>
1252
-
1253
- <IntegrationProfileSheet
1254
- open={isStorageProfileSheetOpen}
1255
- onOpenChange={(nextOpen) => {
1256
- setIsStorageProfileSheetOpen(nextOpen);
1257
- if (!nextOpen) setStorageProfileEditingId(null);
1258
- }}
1259
- profileId={storageProfileEditingId}
1260
- initialTypeSlug="infrastructure"
1261
- lockedTypeSlug="infrastructure"
1262
- onSaved={(profile: IntegrationProfileSheetSavedProfile) => {
1263
- setStorageProfileSelectedLabel(profile.name ?? '');
1264
- setResolvedProfileId(profile.id ?? null);
1265
- handleChangeValue(String(profile.slug ?? ''), true);
1266
- }}
1267
- />
1268
- </>
1269
- );
1270
- }
1271
-
1272
- if (setting.slug === HEDHOG_K8S_INFRA_PROFILE_SETTING_SLUG) {
1273
- return (
1274
- <>
1275
- <div className="flex w-full min-w-0 items-center gap-2 md:max-w-72">
1276
- <EntityPicker<IntegrationProfileOption & Record<string, unknown>>
1277
- placeholder={t('selectK8sInfraProfile')}
1278
- emptyStateDescription={t('k8sInfraProfileEmptyState')}
1279
- value={parsedValue || null}
1280
- onChange={(slug, option) => {
1281
- setStorageProfileSelectedLabel(option?.name ?? '');
1282
- setResolvedProfileId(
1283
- option ? Number((option as { id?: number }).id) || null : null
1284
- );
1285
- handleChangeValue(slug != null ? String(slug) : '', true);
1286
- }}
1287
- valueType="string"
1288
- initialSelectedLabel={storageProfileSelectedLabel}
1289
- showCreateButton={false}
1290
- clearable
1291
- loadOptions={async ({ page, pageSize, search }) => {
1292
- const qs = new URLSearchParams({
1293
- page: String(page),
1294
- pageSize: String(pageSize),
1295
- typeSlug: 'infrastructure',
1296
- providerSlug: 'kubernetes',
1297
- });
1298
- if (search) qs.set('search', search);
1299
- const response = await request<
1300
- ApiPaginated<
1301
- IntegrationProfileOption & Record<string, unknown>
1302
- >
1303
- >({
1304
- url: `/integration-profile?${qs}`,
1305
- method: 'GET',
1306
- });
1307
- const paged = response.data;
1308
- return {
1309
- items: paged?.data ?? [],
1310
- hasMore: (paged?.page ?? 0) < (paged?.lastPage ?? 0),
1311
- };
1312
- }}
1313
- getOptionValue={(opt) => opt.slug}
1314
- getOptionLabel={(opt) => String(opt.name ?? '')}
1315
- getOptionDescription={(opt) => opt.slug}
1316
- className={cn('flex-1 min-w-0', className)}
1317
- />
1318
-
1319
- <Button
1320
- type="button"
1321
- variant="outline"
1322
- size="icon"
1323
- className="h-10 w-10 shrink-0"
1324
- onClick={() => {
1325
- setStorageProfileEditingId(resolvedProfileId);
1326
- setIsStorageProfileSheetOpen(true);
1327
- }}
1328
- disabled={!resolvedProfileId}
1329
- aria-label="Editar perfil Kubernetes"
1330
- title="Editar perfil Kubernetes"
1331
- >
1332
- <Pencil className="h-4 w-4" />
1333
- </Button>
1334
-
1335
- <Button
1336
- type="button"
1337
- variant="outline"
1338
- size="icon"
1339
- className="h-10 w-10 shrink-0"
1340
- onClick={() => {
1341
- setStorageProfileEditingId(null);
1342
- setIsStorageProfileSheetOpen(true);
1343
- }}
1344
- aria-label="Criar perfil Kubernetes"
1345
- title="Criar perfil Kubernetes"
1346
- >
1347
- <Plus className="h-4 w-4" />
1348
- </Button>
1349
- </div>
1350
-
1351
- <IntegrationProfileSheet
1352
- open={isStorageProfileSheetOpen}
1353
- onOpenChange={(nextOpen) => {
1354
- setIsStorageProfileSheetOpen(nextOpen);
1355
- if (!nextOpen) setStorageProfileEditingId(null);
1356
- }}
1357
- profileId={storageProfileEditingId}
1358
- initialTypeSlug="infrastructure"
1359
- lockedTypeSlug="infrastructure"
1360
- lockedProviderSlug="kubernetes"
1361
- onSaved={(profile: IntegrationProfileSheetSavedProfile) => {
1362
- setStorageProfileSelectedLabel(profile.name ?? '');
1363
- setResolvedProfileId(profile.id ?? null);
1364
- handleChangeValue(String(profile.slug ?? ''), true);
1365
- }}
1366
- />
1367
- </>
1368
- );
1369
- }
1370
-
1371
- if (setting.slug === MAIL_INTEGRATION_PROFILE_SETTING_SLUG) {
1372
- return (
1373
- <>
1374
- <div className="flex w-full min-w-0 items-center gap-2 md:max-w-72">
1375
- <EntityPicker<IntegrationProfileOption & Record<string, unknown>>
1376
- placeholder={t('selectMailProfile')}
1377
- emptyStateDescription={t('mailProfileEmptyState')}
1378
- value={parsedValue || null}
1379
- onChange={(slug, option) => {
1380
- setStorageProfileSelectedLabel(option?.name ?? '');
1381
- setResolvedProfileId(
1382
- option ? Number((option as { id?: number }).id) || null : null
1383
- );
1384
- handleChangeValue(slug != null ? String(slug) : '', true);
1385
- }}
1386
- valueType="string"
1387
- initialSelectedLabel={storageProfileSelectedLabel}
1388
- showCreateButton={false}
1389
- clearable={false}
1390
- loadOptions={async ({ page, pageSize, search }) => {
1391
- const qs = new URLSearchParams({
1392
- page: String(page),
1393
- pageSize: String(pageSize),
1394
- typeSlug: 'email',
1395
- });
1396
- if (search) qs.set('search', search);
1397
- const response = await request<
1398
- ApiPaginated<
1399
- IntegrationProfileOption & Record<string, unknown>
1400
- >
1401
- >({
1402
- url: `/integration-profile?${qs}`,
1403
- method: 'GET',
1404
- });
1405
- const paged = response.data;
1406
- return {
1407
- items: paged?.data ?? [],
1408
- hasMore: (paged?.page ?? 0) < (paged?.lastPage ?? 0),
1409
- };
1410
- }}
1411
- getOptionValue={(opt) => opt.slug}
1412
- getOptionLabel={(opt) => String(opt.name ?? '')}
1413
- getOptionDescription={(opt) => {
1414
- const providerSlug = String(
1415
- opt.integration_provider?.slug ?? ''
1416
- );
1417
- const providerName = getLocalizedIntegrationName(
1418
- opt.integration_provider?.integration_provider_locale,
1419
- currentLocaleCode
1420
- );
1421
- return [providerName || providerSlug, opt.slug]
1422
- .filter(Boolean)
1423
- .join(' • ');
1424
- }}
1425
- className={cn('flex-1 min-w-0', className)}
1426
- />
1427
-
1428
- <Button
1429
- type="button"
1430
- variant="outline"
1431
- size="icon"
1432
- className="h-10 w-10 shrink-0"
1433
- onClick={() => {
1434
- setStorageProfileEditingId(resolvedProfileId);
1435
- setIsStorageProfileSheetOpen(true);
1436
- }}
1437
- disabled={!resolvedProfileId}
1438
- aria-label="Editar perfil de email"
1439
- title="Editar perfil de email"
1440
- >
1441
- <Pencil className="h-4 w-4" />
1442
- </Button>
1443
-
1444
- <Button
1445
- type="button"
1446
- variant="outline"
1447
- size="icon"
1448
- className="h-10 w-10 shrink-0"
1449
- onClick={() => {
1450
- setStorageProfileEditingId(null);
1451
- setIsStorageProfileSheetOpen(true);
1452
- }}
1453
- aria-label="Criar perfil de email"
1454
- title="Criar perfil de email"
1455
- >
1456
- <Plus className="h-4 w-4" />
1457
- </Button>
1458
- </div>
843
+ return (
844
+ <Button asChild variant="outline" size="sm" disabled={safeHref === '#'}>
845
+ <Link href={safeHref}>
846
+ <ExternalLink className="h-4 w-4" />
847
+ {t('openLink')}
848
+ </Link>
849
+ </Button>
850
+ );
851
+ }
1459
852
 
1460
- <IntegrationProfileSheet
1461
- open={isStorageProfileSheetOpen}
1462
- onOpenChange={(nextOpen) => {
1463
- setIsStorageProfileSheetOpen(nextOpen);
1464
- if (!nextOpen) setStorageProfileEditingId(null);
1465
- }}
1466
- profileId={storageProfileEditingId}
1467
- initialTypeSlug="email"
1468
- lockedTypeSlug="email"
1469
- onSaved={(profile: IntegrationProfileSheetSavedProfile) => {
1470
- setStorageProfileSelectedLabel(profile.name ?? '');
1471
- setResolvedProfileId(profile.id ?? null);
1472
- handleChangeValue(String(profile.slug ?? ''), true);
1473
- }}
1474
- />
1475
- </>
1476
- );
1477
- }
853
+ case SettingComponentEnum.ENTITY_PICKER: {
854
+ const profileConfig = getIntegrationProfileSettingConfig(setting.slug);
1478
855
 
1479
- if (!dailyReportIntegrationTypeSlug) {
856
+ if (!profileConfig) {
857
+ isTextInputRef.current = true;
1480
858
  return (
1481
859
  <Input
1482
860
  type="text"
@@ -1487,339 +865,148 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
1487
865
  );
1488
866
  }
1489
867
 
1490
- if (dailyReportIntegrationTypeSlug) {
1491
- const loadIntegrationCatalog = async () => {
1492
- const qs = new URLSearchParams({
1493
- typeSlug: dailyReportIntegrationTypeSlug,
1494
- });
1495
- const providersResponse = await request<IntegrationProviderOption[]>({
1496
- url: `/integration-profile/providers?${qs}`,
1497
- method: 'GET',
1498
- });
1499
-
1500
- return {
1501
- providers: (providersResponse.data ??
1502
- []) as IntegrationProviderOption[],
1503
- };
1504
- };
868
+ const { typeSlug, providerSlug, excludeProviders } = profileConfig;
869
+ const translationKeys =
870
+ (typeSlug && PROFILE_TYPE_TRANSLATION_KEYS[typeSlug]) ||
871
+ GENERIC_PROFILE_TRANSLATION_KEYS;
872
+ const profileName = t(
873
+ profileConfig.profileNameKey ?? translationKeys.name
874
+ );
1505
875
 
1506
- return (
1507
- <EntityPicker<IntegrationProfileOption & Record<string, unknown>>
1508
- placeholder={
1509
- dailyReportIntegrationTypeSlug === 'email'
1510
- ? t('selectMailProfile')
1511
- : t('selectWhatsAppProfile')
1512
- }
1513
- value={parsedValue || null}
1514
- onChange={(slug) =>
1515
- handleChangeValue(slug != null ? String(slug) : '', true)
1516
- }
1517
- valueType="string"
1518
- loadOptions={async ({ page, pageSize, search }) => {
1519
- const qs = new URLSearchParams({
1520
- page: String(page),
1521
- pageSize: String(pageSize),
1522
- typeSlug: dailyReportIntegrationTypeSlug,
1523
- });
1524
- if (search) qs.set('search', search);
1525
- const response = await request<
1526
- ApiPaginated<IntegrationProfileOption & Record<string, unknown>>
1527
- >({
1528
- url: `/integration-profile?${qs}`,
1529
- method: 'GET',
1530
- });
1531
- const paged = response.data;
1532
- return {
1533
- items: paged?.data ?? [],
1534
- hasMore: (paged?.page ?? 0) < (paged?.lastPage ?? 0),
1535
- };
1536
- }}
1537
- getOptionValue={(opt) => opt.slug}
1538
- getOptionLabel={(opt) => String(opt.name ?? '')}
1539
- getOptionDescription={(opt) =>
1540
- String(opt.integration_provider?.slug ?? opt.slug ?? '')
1541
- }
1542
- mapSearchToCreateValues={(search) =>
1543
- dailyReportIntegrationTypeSlug === 'email'
1544
- ? {
1545
- name: search,
1546
- provider: 'smtp',
1547
- port: '587',
1548
- }
1549
- : {
1550
- name: search,
1551
- provider: 'evolution-api',
1552
- port: '',
1553
- }
1554
- }
1555
- showCreateButton
1556
- onCreate={async (values) => {
1557
- const providersCatalog = await loadIntegrationCatalog();
1558
- const providerSlug =
1559
- dailyReportIntegrationTypeSlug === 'email'
1560
- ? values.provider || 'smtp'
1561
- : 'evolution-api';
1562
- const provider = providersCatalog.providers.find(
1563
- (item) => item.slug === providerSlug
1564
- );
876
+ // O picker exibe o nome do perfil, mas a setting guarda o slug.
877
+ const applyProfile = (
878
+ profile: {
879
+ id?: number | null;
880
+ slug?: string | null;
881
+ name?: string | null;
882
+ } | null
883
+ ) => {
884
+ setStorageProfileSelectedLabel(profile?.name ?? '');
885
+ setResolvedProfileId(profile?.id ?? null);
886
+ handleChangeValue(profile?.slug ? String(profile.slug) : '', true);
887
+ };
1565
888
 
1566
- if (!provider) {
1567
- throw new Error(
1568
- `Integration provider "${providerSlug}" not found.`
1569
- );
889
+ return (
890
+ <>
891
+ <div className="flex w-full min-w-0 items-center gap-2 md:max-w-72">
892
+ <EntityPicker<IntegrationProfileOption & Record<string, unknown>>
893
+ placeholder={t(translationKeys.placeholder)}
894
+ emptyStateDescription={t(translationKeys.emptyState)}
895
+ value={parsedValue || null}
896
+ onChange={(slug, option) =>
897
+ applyProfile(
898
+ option
899
+ ? {
900
+ id: Number((option as { id?: number }).id) || null,
901
+ slug: slug != null ? String(slug) : '',
902
+ name: option.name ?? '',
903
+ }
904
+ : null
905
+ )
1570
906
  }
1571
-
1572
- const name = String(values.name || 'Profile').trim();
1573
- const response = await request<
1574
- IntegrationProfileOption & Record<string, unknown>
1575
- >({
1576
- url: '/integration-profile',
1577
- method: 'POST',
1578
- data: {
1579
- name,
1580
- slug: toSlug(name),
1581
- type_id: provider.type_id,
1582
- provider_id: provider.id,
1583
- config: buildIntegrationProfileConfig(providerSlug, values),
1584
- is_active: true,
1585
- },
1586
- });
1587
- return response.data;
1588
- }}
1589
- renderCreateContent={({ values, setValue }) => {
1590
- if (dailyReportIntegrationTypeSlug === 'whatsapp') {
1591
- return (
1592
- <div className="space-y-4">
1593
- <div className="space-y-1">
1594
- <Label>{t('whatsappProfile.nameLabel')}</Label>
1595
- <Input
1596
- value={values.name || ''}
1597
- onChange={(e) => setValue('name', e.target.value)}
1598
- placeholder={t('whatsappProfile.namePlaceholder')}
1599
- />
1600
- </div>
1601
- <div className="space-y-1">
1602
- <Label>{t('whatsappProfile.providerLabel')}</Label>
1603
- <Select
1604
- value={values.provider || 'evolution-api'}
1605
- onValueChange={(v) => setValue('provider', v)}
1606
- >
1607
- <SelectTrigger className="w-full">
1608
- <SelectValue />
1609
- </SelectTrigger>
1610
- <SelectContent>
1611
- <SelectItem value="evolution-api">
1612
- Evolution API
1613
- </SelectItem>
1614
- </SelectContent>
1615
- </Select>
1616
- </div>
1617
- <div className="space-y-1">
1618
- <Label>{t('whatsappProfile.hostLabel')}</Label>
1619
- <Input
1620
- value={values.host || ''}
1621
- onChange={(e) => setValue('host', e.target.value)}
1622
- placeholder={t('whatsappProfile.hostPlaceholder')}
1623
- />
1624
- </div>
1625
- <div className="space-y-1">
1626
- <Label>{t('whatsappProfile.instanceNameLabel')}</Label>
1627
- <Input
1628
- value={values.instance_name || ''}
1629
- onChange={(e) =>
1630
- setValue('instance_name', e.target.value)
1631
- }
1632
- placeholder={t(
1633
- 'whatsappProfile.instanceNamePlaceholder'
1634
- )}
1635
- />
1636
- </div>
1637
- <div className="space-y-1">
1638
- <Label>{t('whatsappProfile.tokenLabel')}</Label>
1639
- <Input
1640
- type="password"
1641
- value={values.token || ''}
1642
- onChange={(e) => setValue('token', e.target.value)}
1643
- placeholder={t('whatsappProfile.tokenPlaceholder')}
1644
- />
1645
- </div>
1646
- </div>
907
+ valueType="string"
908
+ initialSelectedLabel={storageProfileSelectedLabel}
909
+ showCreateButton={false}
910
+ clearable={false}
911
+ loadOptions={async ({ page, pageSize, search }) => {
912
+ const qs = new URLSearchParams({
913
+ page: String(page),
914
+ pageSize: String(pageSize),
915
+ });
916
+ if (typeSlug) qs.set('typeSlug', typeSlug);
917
+ if (providerSlug) qs.set('providerSlug', providerSlug);
918
+ if (search) qs.set('search', search);
919
+
920
+ const response = await request<
921
+ ApiPaginated<
922
+ IntegrationProfileOption & Record<string, unknown>
923
+ >
924
+ >({
925
+ url: `/integration-profile?${qs}`,
926
+ method: 'GET',
927
+ });
928
+ const paged = response.data;
929
+
930
+ return {
931
+ // O endpoint filtra por tipo e por provider; o que sobra
932
+ // (Google Play como gateway do checkout web, por exemplo) sai
933
+ // aqui para não oferecer uma escolha que o backend recusaria.
934
+ items: (paged?.data ?? []).filter(
935
+ (profile) =>
936
+ !excludeProviders?.includes(
937
+ String(profile.integration_provider?.slug ?? '')
938
+ )
939
+ ),
940
+ hasMore: (paged?.page ?? 0) < (paged?.lastPage ?? 0),
941
+ };
942
+ }}
943
+ getOptionValue={(opt) => opt.slug}
944
+ getOptionLabel={(opt) => String(opt.name ?? '')}
945
+ getOptionDescription={(opt) => {
946
+ const optionProviderSlug = String(
947
+ opt.integration_provider?.slug ?? ''
1647
948
  );
1648
- }
949
+ const providerName = getLocalizedIntegrationName(
950
+ opt.integration_provider?.integration_provider_locale,
951
+ currentLocaleCode
952
+ );
953
+ return [providerName || optionProviderSlug, opt.slug]
954
+ .filter(Boolean)
955
+ .join(' • ');
956
+ }}
957
+ className={cn('flex-1 min-w-0', className)}
958
+ />
1649
959
 
1650
- const provider = values.provider || 'smtp';
1651
- return (
1652
- <div className="space-y-4">
1653
- <div className="space-y-1">
1654
- <Label>{t('mailProfile.nameLabel')}</Label>
1655
- <Input
1656
- value={values.name || ''}
1657
- onChange={(e) => setValue('name', e.target.value)}
1658
- placeholder={t('mailProfile.namePlaceholder')}
1659
- />
1660
- </div>
1661
- <div className="space-y-1">
1662
- <Label>{t('mailProfile.providerLabel')}</Label>
1663
- <Select
1664
- value={provider}
1665
- onValueChange={(v) => setValue('provider', v)}
1666
- >
1667
- <SelectTrigger className="w-full">
1668
- <SelectValue />
1669
- </SelectTrigger>
1670
- <SelectContent>
1671
- <SelectItem value="smtp">SMTP</SelectItem>
1672
- <SelectItem value="gmail">Gmail</SelectItem>
1673
- <SelectItem value="ses">
1674
- {t('mailProfile.providers.ses')}
1675
- </SelectItem>
1676
- </SelectContent>
1677
- </Select>
1678
- </div>
1679
- <div className="space-y-1">
1680
- <Label>{t('mailProfile.fromEmailLabel')}</Label>
1681
- <Input
1682
- type="email"
1683
- value={values.from_email || ''}
1684
- onChange={(e) => setValue('from_email', e.target.value)}
1685
- placeholder={t('mailProfile.fromEmailPlaceholder')}
1686
- />
1687
- </div>
1688
- <div className="space-y-1">
1689
- <Label>{t('mailProfile.fromNameLabel')}</Label>
1690
- <Input
1691
- value={values.from_name || ''}
1692
- onChange={(e) => setValue('from_name', e.target.value)}
1693
- placeholder={t('mailProfile.fromNamePlaceholder')}
1694
- />
1695
- </div>
1696
- {provider === 'smtp' && (
1697
- <>
1698
- <div className="space-y-1">
1699
- <Label>{t('mailProfile.smtpHostLabel')}</Label>
1700
- <Input
1701
- value={values.host || ''}
1702
- onChange={(e) => setValue('host', e.target.value)}
1703
- placeholder={t('mailProfile.smtpHostPlaceholder')}
1704
- />
1705
- </div>
1706
- <div className="space-y-1">
1707
- <Label>{t('mailProfile.smtpPortLabel')}</Label>
1708
- <Input
1709
- type="number"
1710
- value={values.port || '587'}
1711
- onChange={(e) => setValue('port', e.target.value)}
1712
- placeholder={t('mailProfile.smtpPortPlaceholder')}
1713
- />
1714
- </div>
1715
- <div className="space-y-1">
1716
- <Label>{t('mailProfile.smtpUsernameLabel')}</Label>
1717
- <Input
1718
- value={values.username || ''}
1719
- onChange={(e) => setValue('username', e.target.value)}
1720
- placeholder={t('mailProfile.smtpUsernamePlaceholder')}
1721
- />
1722
- </div>
1723
- <div className="space-y-1">
1724
- <Label>{t('mailProfile.smtpPasswordLabel')}</Label>
1725
- <Input
1726
- type="password"
1727
- value={values.password || ''}
1728
- onChange={(e) => setValue('password', e.target.value)}
1729
- />
1730
- </div>
1731
- <div className="space-y-1">
1732
- <Label>{t('mailProfile.replyToEmailLabel')}</Label>
1733
- <Input
1734
- type="email"
1735
- value={values.reply_to_email || ''}
1736
- onChange={(e) =>
1737
- setValue('reply_to_email', e.target.value)
1738
- }
1739
- placeholder={t('mailProfile.replyToEmailPlaceholder')}
1740
- />
1741
- </div>
1742
- </>
1743
- )}
1744
- {provider === 'gmail' && (
1745
- <>
1746
- <div className="space-y-1">
1747
- <Label>{t('mailProfile.clientIdLabel')}</Label>
1748
- <Input
1749
- value={values.client_id || ''}
1750
- onChange={(e) =>
1751
- setValue('client_id', e.target.value)
1752
- }
1753
- />
1754
- </div>
1755
- <div className="space-y-1">
1756
- <Label>{t('mailProfile.clientSecretLabel')}</Label>
1757
- <Input
1758
- type="password"
1759
- value={values.client_secret || ''}
1760
- onChange={(e) =>
1761
- setValue('client_secret', e.target.value)
1762
- }
1763
- />
1764
- </div>
1765
- <div className="space-y-1">
1766
- <Label>{t('mailProfile.refreshTokenLabel')}</Label>
1767
- <Input
1768
- value={values.refresh_token || ''}
1769
- onChange={(e) =>
1770
- setValue('refresh_token', e.target.value)
1771
- }
1772
- />
1773
- </div>
1774
- </>
1775
- )}
1776
- {provider === 'ses' && (
1777
- <>
1778
- <div className="space-y-1">
1779
- <Label>{t('mailProfile.accessKeyIdLabel')}</Label>
1780
- <Input
1781
- value={values.access_key_id || ''}
1782
- onChange={(e) =>
1783
- setValue('access_key_id', e.target.value)
1784
- }
1785
- />
1786
- </div>
1787
- <div className="space-y-1">
1788
- <Label>{t('mailProfile.secretAccessKeyLabel')}</Label>
1789
- <Input
1790
- type="password"
1791
- value={values.secret_access_key || ''}
1792
- onChange={(e) =>
1793
- setValue('secret_access_key', e.target.value)
1794
- }
1795
- />
1796
- </div>
1797
- <div className="space-y-1">
1798
- <Label>{t('mailProfile.regionLabel')}</Label>
1799
- <Input
1800
- value={values.region || ''}
1801
- onChange={(e) => setValue('region', e.target.value)}
1802
- placeholder={t('mailProfile.regionPlaceholder')}
1803
- />
1804
- </div>
1805
- </>
1806
- )}
1807
- </div>
1808
- );
960
+ <Button
961
+ type="button"
962
+ variant="outline"
963
+ size="icon"
964
+ className="h-10 w-10 shrink-0"
965
+ onClick={() => {
966
+ setStorageProfileEditingId(resolvedProfileId);
967
+ setIsStorageProfileSheetOpen(true);
968
+ }}
969
+ disabled={!resolvedProfileId}
970
+ aria-label={t('editProfileAria', { profile: profileName })}
971
+ title={t('editProfileAria', { profile: profileName })}
972
+ >
973
+ <Pencil className="h-4 w-4" />
974
+ </Button>
975
+
976
+ <Button
977
+ type="button"
978
+ variant="outline"
979
+ size="icon"
980
+ className="h-10 w-10 shrink-0"
981
+ onClick={() => {
982
+ setStorageProfileEditingId(null);
983
+ setIsStorageProfileSheetOpen(true);
984
+ }}
985
+ aria-label={t('createProfileAria', { profile: profileName })}
986
+ title={t('createProfileAria', { profile: profileName })}
987
+ >
988
+ <Plus className="h-4 w-4" />
989
+ </Button>
990
+ </div>
991
+
992
+ <IntegrationProfileSheet
993
+ open={isStorageProfileSheetOpen}
994
+ onOpenChange={(nextOpen) => {
995
+ setIsStorageProfileSheetOpen(nextOpen);
996
+ if (!nextOpen) setStorageProfileEditingId(null);
1809
997
  }}
1810
- className={cn(className, 'w-full min-w-0 md:min-w-50')}
998
+ profileId={storageProfileEditingId}
999
+ initialTypeSlug={typeSlug}
1000
+ lockedTypeSlug={typeSlug}
1001
+ lockedProviderSlug={providerSlug}
1002
+ excludeProviderSlugs={excludeProviders}
1003
+ onSaved={(profile: IntegrationProfileSheetSavedProfile) =>
1004
+ applyProfile(profile)
1005
+ }
1811
1006
  />
1812
- );
1813
- }
1814
-
1815
- return (
1816
- <Input
1817
- type="text"
1818
- value={parsedValue || ''}
1819
- onChange={(e) => handleChangeValue(formatValue(e.target.value))}
1820
- className={cn(className, 'bg-background')}
1821
- />
1007
+ </>
1822
1008
  );
1009
+ }
1823
1010
 
1824
1011
  default:
1825
1012
  return (