@stackcraftmx/calcita 0.32.2 → 0.51.0

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 (680) hide show
  1. package/README.md +426 -4
  2. package/cli/calcita.js +15 -0
  3. package/cli/checkTexts.js +209 -0
  4. package/cli/packageVersions.js +17 -3
  5. package/cli/projectFiles.js +7 -2
  6. package/cli/templates/module/__Name__Page.test.jsx.tpl +24 -0
  7. package/cli/templates/project/README.md.tpl +35 -2
  8. package/cli/templates/project/eslint.config.js.tpl +27 -0
  9. package/cli/templates/project/src/app/appRouter.jsx.tpl +5 -2
  10. package/cli/templates/project/src/app/appTexts.js.tpl +2 -1
  11. package/cli/templates/project/src/core/accountConnector.js.tpl +4 -2
  12. package/cli/templates/project/src/core/publicPaths.js.tpl +10 -2
  13. package/cli/templates/project/src/modules/auth/SignInPage.test.jsx.tpl +20 -0
  14. package/cli/templates/project/src/modules/home/HomePage.test.jsx.tpl +16 -0
  15. package/cli/templates/project/src/testing/browserGaps.js.tpl +70 -0
  16. package/cli/templates/project/src/testing/openScreen.jsx.tpl +58 -0
  17. package/cli/templates/project/src/testing/setup.js.tpl +25 -0
  18. package/cli/templates/project/vite.config.js.tpl +20 -1
  19. package/dist/DataTable/AuditTrailDialog.js +3 -2
  20. package/dist/DataTable/AuditTrailDialog.js.map +1 -1
  21. package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js +3 -2
  22. package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js.map +1 -1
  23. package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js +3 -2
  24. package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js.map +1 -1
  25. package/dist/DataTable/ColumnFilters/SortableFilterHeader.js +3 -2
  26. package/dist/DataTable/ColumnFilters/SortableFilterHeader.js.map +1 -1
  27. package/dist/DataTable/EmptyState.js +3 -2
  28. package/dist/DataTable/EmptyState.js.map +1 -1
  29. package/dist/DataTable/FilterChips.js +3 -2
  30. package/dist/DataTable/FilterChips.js.map +1 -1
  31. package/dist/DataTable/GoToPage.js +71 -14
  32. package/dist/DataTable/GoToPage.js.map +1 -1
  33. package/dist/DataTable/ServerTable.js +97 -28
  34. package/dist/DataTable/ServerTable.js.map +1 -1
  35. package/dist/DataTable/ServerTableFilterDrawer.js +57 -0
  36. package/dist/DataTable/ServerTableFilterDrawer.js.map +1 -0
  37. package/dist/DataTable/StackedCell.js +60 -0
  38. package/dist/DataTable/StackedCell.js.map +1 -0
  39. package/dist/DataTable/TableCardsView.js +88 -0
  40. package/dist/DataTable/TableCardsView.js.map +1 -0
  41. package/dist/DataTable/columnWidths.js +30 -0
  42. package/dist/DataTable/columnWidths.js.map +1 -0
  43. package/dist/DataTable/columns.js +47 -1
  44. package/dist/DataTable/columns.js.map +1 -1
  45. package/dist/DataTable/filterContext.js.map +1 -1
  46. package/dist/DataTable/fixedColumns.js +24 -0
  47. package/dist/DataTable/fixedColumns.js.map +1 -0
  48. package/dist/DataTable/goToPageOptions.js +17 -0
  49. package/dist/DataTable/goToPageOptions.js.map +1 -0
  50. package/dist/DataTable/useTableView.js +29 -0
  51. package/dist/DataTable/useTableView.js.map +1 -0
  52. package/dist/access/PermissionMatrix.js +3 -2
  53. package/dist/access/PermissionMatrix.js.map +1 -1
  54. package/dist/account/AccountProvider.js +1 -1
  55. package/dist/account/AccountSecuritySection.js +3 -2
  56. package/dist/account/AccountSecuritySection.js.map +1 -1
  57. package/dist/account/DemoTotpCodeButton.js +3 -2
  58. package/dist/account/DemoTotpCodeButton.js.map +1 -1
  59. package/dist/account/ProfileScreen.js +3 -2
  60. package/dist/account/ProfileScreen.js.map +1 -1
  61. package/dist/account/SignInScreen.js +3 -2
  62. package/dist/account/SignInScreen.js.map +1 -1
  63. package/dist/appShell/AccountMenu.js +3 -2
  64. package/dist/appShell/AccountMenu.js.map +1 -1
  65. package/dist/appShell/AppLayout.js +3 -2
  66. package/dist/appShell/AppLayout.js.map +1 -1
  67. package/dist/appShell/AppearanceSettings.js +3 -2
  68. package/dist/appShell/AppearanceSettings.js.map +1 -1
  69. package/dist/appShell/AuthLayout.js +3 -2
  70. package/dist/appShell/AuthLayout.js.map +1 -1
  71. package/dist/appShell/BarActionButton.js +11 -4
  72. package/dist/appShell/BarActionButton.js.map +1 -1
  73. package/dist/appShell/BarActions.js +3 -2
  74. package/dist/appShell/BarActions.js.map +1 -1
  75. package/dist/appShell/BarCapsule.js +3 -2
  76. package/dist/appShell/BarCapsule.js.map +1 -1
  77. package/dist/appShell/BarSearch.js +3 -2
  78. package/dist/appShell/BarSearch.js.map +1 -1
  79. package/dist/appShell/HeaderTools.js +3 -2
  80. package/dist/appShell/HeaderTools.js.map +1 -1
  81. package/dist/appShell/MobileTabBar.js +3 -2
  82. package/dist/appShell/MobileTabBar.js.map +1 -1
  83. package/dist/appShell/SettingsMenu.js +3 -2
  84. package/dist/appShell/SettingsMenu.js.map +1 -1
  85. package/dist/appShell/SideMenu.js +3 -2
  86. package/dist/appShell/SideMenu.js.map +1 -1
  87. package/dist/appShell/SideMenuEntry.js +3 -2
  88. package/dist/appShell/SideMenuEntry.js.map +1 -1
  89. package/dist/appShell/SubmenuActionsMenu.js +3 -2
  90. package/dist/appShell/SubmenuActionsMenu.js.map +1 -1
  91. package/dist/appShell/TabBarEditor.js +3 -2
  92. package/dist/appShell/TabBarEditor.js.map +1 -1
  93. package/dist/appShell/TopMenu.js +3 -2
  94. package/dist/appShell/TopMenu.js.map +1 -1
  95. package/dist/appShell/Wordmark.js +3 -2
  96. package/dist/appShell/Wordmark.js.map +1 -1
  97. package/dist/auth/PasswordChecklist.js +3 -2
  98. package/dist/auth/PasswordChecklist.js.map +1 -1
  99. package/dist/calendar/EventCalendar.js +3 -2
  100. package/dist/calendar/EventCalendar.js.map +1 -1
  101. package/dist/calendar/EventDetails.js +3 -2
  102. package/dist/calendar/EventDetails.js.map +1 -1
  103. package/dist/date-pickers.js +28 -2
  104. package/dist/date-pickers.js.map +1 -1
  105. package/dist/datePickers/DateInput.js +41 -54
  106. package/dist/datePickers/DateInput.js.map +1 -1
  107. package/dist/datePickers/DateTimeInput.js +81 -0
  108. package/dist/datePickers/DateTimeInput.js.map +1 -0
  109. package/dist/datePickers/FormDateTimeField.js +12 -0
  110. package/dist/datePickers/FormDateTimeField.js.map +1 -0
  111. package/dist/datePickers/FormMonthField.js +12 -0
  112. package/dist/datePickers/FormMonthField.js.map +1 -0
  113. package/dist/datePickers/FormTimeField.js +12 -0
  114. package/dist/datePickers/FormTimeField.js.map +1 -0
  115. package/dist/datePickers/MonthInput.js +64 -0
  116. package/dist/datePickers/MonthInput.js.map +1 -0
  117. package/dist/datePickers/PickerFrame.js +27 -0
  118. package/dist/datePickers/PickerFrame.js.map +1 -0
  119. package/dist/datePickers/TimeInput.js +61 -0
  120. package/dist/datePickers/TimeInput.js.map +1 -0
  121. package/dist/datePickers/dateTimeRules.js +24 -0
  122. package/dist/datePickers/dateTimeRules.js.map +1 -0
  123. package/dist/datePickers/pickerValues.js +106 -0
  124. package/dist/datePickers/pickerValues.js.map +1 -0
  125. package/dist/datePickers/useCurrentMinute.js +28 -0
  126. package/dist/datePickers/useCurrentMinute.js.map +1 -0
  127. package/dist/datePickers/usePickerField.js +23 -0
  128. package/dist/datePickers/usePickerField.js.map +1 -0
  129. package/dist/dictionaries/dictionaries.js +3 -1
  130. package/dist/dictionaries/dictionaries.js.map +1 -1
  131. package/dist/downloads/DownloadManager.js +3 -2
  132. package/dist/downloads/DownloadManager.js.map +1 -1
  133. package/dist/feedback/ConfirmDialog.js +3 -2
  134. package/dist/feedback/ConfirmDialog.js.map +1 -1
  135. package/dist/feedback/ModalWaiting.js +3 -2
  136. package/dist/feedback/ModalWaiting.js.map +1 -1
  137. package/dist/feedback/NewBuildNotice.js +3 -2
  138. package/dist/feedback/NewBuildNotice.js.map +1 -1
  139. package/dist/filepond/FileDropzone.js +3 -2
  140. package/dist/filepond/FileDropzone.js.map +1 -1
  141. package/dist/forms/FormAttachmentsField.js +12 -0
  142. package/dist/forms/FormAttachmentsField.js.map +1 -0
  143. package/dist/forms/FormCheckboxGroupField.js +12 -0
  144. package/dist/forms/FormCheckboxGroupField.js.map +1 -0
  145. package/dist/forms/FormFileField.js +12 -0
  146. package/dist/forms/FormFileField.js.map +1 -0
  147. package/dist/forms/FormFileUploadField.js +12 -0
  148. package/dist/forms/FormFileUploadField.js.map +1 -0
  149. package/dist/forms/FormImageField.js +12 -0
  150. package/dist/forms/FormImageField.js.map +1 -0
  151. package/dist/forms/FormMaskedField.js +12 -0
  152. package/dist/forms/FormMaskedField.js.map +1 -0
  153. package/dist/forms/FormProblemAlert.js +3 -2
  154. package/dist/forms/FormProblemAlert.js.map +1 -1
  155. package/dist/forms/FormRepeatingFields.js +54 -0
  156. package/dist/forms/FormRepeatingFields.js.map +1 -0
  157. package/dist/forms/FormTransferListField.js +12 -0
  158. package/dist/forms/FormTransferListField.js.map +1 -0
  159. package/dist/forms/SubmitButton.js +7 -2
  160. package/dist/forms/SubmitButton.js.map +1 -1
  161. package/dist/forms/applyServerProblem.js +10 -3
  162. package/dist/forms/applyServerProblem.js.map +1 -1
  163. package/dist/forms/auth/SignInForm.js +3 -2
  164. package/dist/forms/auth/SignInForm.js.map +1 -1
  165. package/dist/forms/fieldRules.js +30 -0
  166. package/dist/forms/fieldRules.js.map +1 -0
  167. package/dist/forms/listRules.js +111 -0
  168. package/dist/forms/listRules.js.map +1 -0
  169. package/dist/forms/security/SecondStepForm.js +3 -2
  170. package/dist/forms/security/SecondStepForm.js.map +1 -1
  171. package/dist/forms/security/TwoStepVerificationCard.js +3 -2
  172. package/dist/forms/security/TwoStepVerificationCard.js.map +1 -1
  173. package/dist/forms/serverProblemFields.js +17 -0
  174. package/dist/forms/serverProblemFields.js.map +1 -1
  175. package/dist/forms/useValidationText.js +18 -0
  176. package/dist/forms/useValidationText.js.map +1 -0
  177. package/dist/forms/validation.js +35 -31
  178. package/dist/forms/validation.js.map +1 -1
  179. package/dist/forms/validationMessages.js +64 -7
  180. package/dist/forms/validationMessages.js.map +1 -1
  181. package/dist/forms.js +54 -29
  182. package/dist/forms.js.map +1 -1
  183. package/dist/hookForm/FormAttachmentsField.js +12 -0
  184. package/dist/hookForm/FormAttachmentsField.js.map +1 -0
  185. package/dist/hookForm/FormCheckboxGroupField.js +12 -0
  186. package/dist/hookForm/FormCheckboxGroupField.js.map +1 -0
  187. package/dist/hookForm/FormFileField.js +12 -0
  188. package/dist/hookForm/FormFileField.js.map +1 -0
  189. package/dist/hookForm/FormFileUploadField.js +12 -0
  190. package/dist/hookForm/FormFileUploadField.js.map +1 -0
  191. package/dist/hookForm/FormImageField.js +12 -0
  192. package/dist/hookForm/FormImageField.js.map +1 -0
  193. package/dist/hookForm/FormMaskedField.js +12 -0
  194. package/dist/hookForm/FormMaskedField.js.map +1 -0
  195. package/dist/hookForm/FormProblemAlert.js +3 -2
  196. package/dist/hookForm/FormProblemAlert.js.map +1 -1
  197. package/dist/hookForm/FormRepeatingFields.js +44 -0
  198. package/dist/hookForm/FormRepeatingFields.js.map +1 -0
  199. package/dist/hookForm/FormTransferListField.js +12 -0
  200. package/dist/hookForm/FormTransferListField.js.map +1 -0
  201. package/dist/hookForm/SubmitButton.js +7 -2
  202. package/dist/hookForm/SubmitButton.js.map +1 -1
  203. package/dist/hookForm/applyServerProblem.js +8 -3
  204. package/dist/hookForm/applyServerProblem.js.map +1 -1
  205. package/dist/inbox/InboxLayout.js +3 -2
  206. package/dist/inbox/InboxLayout.js.map +1 -1
  207. package/dist/inbox/MessageList.js +3 -2
  208. package/dist/inbox/MessageList.js.map +1 -1
  209. package/dist/inbox/MessageView.js +3 -2
  210. package/dist/inbox/MessageView.js.map +1 -1
  211. package/dist/index.js +235 -124
  212. package/dist/index.js.map +1 -1
  213. package/dist/inputs/CatalogInput.js +103 -29
  214. package/dist/inputs/CatalogInput.js.map +1 -1
  215. package/dist/inputs/CheckboxGroupInput.js +103 -0
  216. package/dist/inputs/CheckboxGroupInput.js.map +1 -0
  217. package/dist/inputs/ColorInput.js +7 -4
  218. package/dist/inputs/ColorInput.js.map +1 -1
  219. package/dist/inputs/FieldFrame.js +10 -4
  220. package/dist/inputs/FieldFrame.js.map +1 -1
  221. package/dist/inputs/FormSection.js +1 -1
  222. package/dist/inputs/MaskedInput.js +87 -0
  223. package/dist/inputs/MaskedInput.js.map +1 -0
  224. package/dist/inputs/NumberInput.js +41 -25
  225. package/dist/inputs/NumberInput.js.map +1 -1
  226. package/dist/inputs/RepeatingFields.js +180 -0
  227. package/dist/inputs/RepeatingFields.js.map +1 -0
  228. package/dist/inputs/SelectInput.js +50 -9
  229. package/dist/inputs/SelectInput.js.map +1 -1
  230. package/dist/inputs/TextInput.js +5 -3
  231. package/dist/inputs/TextInput.js.map +1 -1
  232. package/dist/inputs/TransferListInput.js +218 -0
  233. package/dist/inputs/TransferListInput.js.map +1 -0
  234. package/dist/inputs/WizardLayout.js +3 -2
  235. package/dist/inputs/WizardLayout.js.map +1 -1
  236. package/dist/inputs/inputMasks.js +84 -0
  237. package/dist/inputs/inputMasks.js.map +1 -0
  238. package/dist/inputs/selectAllState.js +20 -0
  239. package/dist/inputs/selectAllState.js.map +1 -0
  240. package/dist/inputs/useRowKeys.js +26 -0
  241. package/dist/inputs/useRowKeys.js.map +1 -0
  242. package/dist/kanban/KanbanBoard.js +3 -2
  243. package/dist/kanban/KanbanBoard.js.map +1 -1
  244. package/dist/legal/CookieNotice.js +80 -0
  245. package/dist/legal/CookieNotice.js.map +1 -0
  246. package/dist/legal/TermsAcceptanceDialog.js +130 -0
  247. package/dist/legal/TermsAcceptanceDialog.js.map +1 -0
  248. package/dist/legal/consentStorage.js +18 -0
  249. package/dist/legal/consentStorage.js.map +1 -0
  250. package/dist/legal/useCookieConsent.js +17 -0
  251. package/dist/legal/useCookieConsent.js.map +1 -0
  252. package/dist/locales/en.js +257 -1
  253. package/dist/locales/en.js.map +1 -1
  254. package/dist/locales/es.js +257 -1
  255. package/dist/locales/es.js.map +1 -1
  256. package/dist/maps/MapView.js +3 -2
  257. package/dist/maps/MapView.js.map +1 -1
  258. package/dist/mexico/CfdiSummary.js +82 -0
  259. package/dist/mexico/CfdiSummary.js.map +1 -0
  260. package/dist/mexico/EfirmaFields.js +168 -0
  261. package/dist/mexico/EfirmaFields.js.map +1 -0
  262. package/dist/mexico/certificateReader.js +79 -0
  263. package/dist/mexico/certificateReader.js.map +1 -0
  264. package/dist/mexico/cfdiReader.js +81 -0
  265. package/dist/mexico/cfdiReader.js.map +1 -0
  266. package/dist/mexico/derReader.js +81 -0
  267. package/dist/mexico/derReader.js.map +1 -0
  268. package/dist/mexico/efirmaKey.js +51 -0
  269. package/dist/mexico/efirmaKey.js.map +1 -0
  270. package/dist/mexico/mexicoIdentifiers.js +91 -0
  271. package/dist/mexico/mexicoIdentifiers.js.map +1 -0
  272. package/dist/mexico/mexicoRules.js +21 -0
  273. package/dist/mexico/mexicoRules.js.map +1 -0
  274. package/dist/mexico.js +34 -0
  275. package/dist/mexico.js.map +1 -0
  276. package/dist/pwa/InstallAppSetting.js +3 -2
  277. package/dist/pwa/InstallAppSetting.js.map +1 -1
  278. package/dist/pwa/InstallInstructionsDialog.js +3 -2
  279. package/dist/pwa/InstallInstructionsDialog.js.map +1 -1
  280. package/dist/pwa/InstallPromptBanner.js +3 -2
  281. package/dist/pwa/InstallPromptBanner.js.map +1 -1
  282. package/dist/react-hook-form.js +43 -17
  283. package/dist/react-hook-form.js.map +1 -1
  284. package/dist/rich-text.js +19 -0
  285. package/dist/rich-text.js.map +1 -0
  286. package/dist/richText/FormRichTextField.js +12 -0
  287. package/dist/richText/FormRichTextField.js.map +1 -0
  288. package/dist/richText/RichTextEditor.js +111 -0
  289. package/dist/richText/RichTextEditor.js.map +1 -0
  290. package/dist/richText/RichTextLinkPopover.js +57 -0
  291. package/dist/richText/RichTextLinkPopover.js.map +1 -0
  292. package/dist/richText/RichTextToolbar.js +189 -0
  293. package/dist/richText/RichTextToolbar.js.map +1 -0
  294. package/dist/richText/RichTextView.js +27 -0
  295. package/dist/richText/RichTextView.js.map +1 -0
  296. package/dist/richText/richTextContentSx.js +42 -0
  297. package/dist/richText/richTextContentSx.js.map +1 -0
  298. package/dist/richText/richTextExtensions.js +44 -0
  299. package/dist/richText/richTextExtensions.js.map +1 -0
  300. package/dist/richText/richTextRules.js +46 -0
  301. package/dist/richText/richTextRules.js.map +1 -0
  302. package/dist/security/ActivityLog.js +3 -2
  303. package/dist/security/ActivityLog.js.map +1 -1
  304. package/dist/security/RecoveryCodesDialog.js +3 -2
  305. package/dist/security/RecoveryCodesDialog.js.map +1 -1
  306. package/dist/security/SessionsCard.js +3 -2
  307. package/dist/security/SessionsCard.js.map +1 -1
  308. package/dist/table.js +23 -2
  309. package/dist/table.js.map +1 -1
  310. package/dist/theme/components/glassComponents.js +27 -0
  311. package/dist/theme/components/glassComponents.js.map +1 -1
  312. package/dist/theme/components/minimalComponents.js +12 -0
  313. package/dist/theme/components/minimalComponents.js.map +1 -1
  314. package/dist/theme/themePieces.js +70 -4
  315. package/dist/theme/themePieces.js.map +1 -1
  316. package/dist/toolkit/ToolkitProvider.js +8 -1
  317. package/dist/toolkit/ToolkitProvider.js.map +1 -1
  318. package/dist/toolkit/browserNavigation.js +19 -4
  319. package/dist/toolkit/browserNavigation.js.map +1 -1
  320. package/dist/toolkit/combineTexts.js +32 -0
  321. package/dist/toolkit/combineTexts.js.map +1 -0
  322. package/dist/toolkit/hashNavigation.js +17 -3
  323. package/dist/toolkit/hashNavigation.js.map +1 -1
  324. package/dist/toolkit/navigationPaths.js +15 -0
  325. package/dist/toolkit/navigationPaths.js.map +1 -1
  326. package/dist/toolkit/pathIndex.js +66 -0
  327. package/dist/toolkit/pathIndex.js.map +1 -0
  328. package/dist/toolkit/toolkitSettings.js +19 -1
  329. package/dist/toolkit/toolkitSettings.js.map +1 -1
  330. package/dist/types/DataTable/AuditTrailDialog.d.ts +6 -17
  331. package/dist/types/DataTable/ColumnFilters/HeaderFilterPopover.d.ts +6 -5
  332. package/dist/types/DataTable/ColumnFilters/MultiSelectColumnFilter.d.ts +6 -9
  333. package/dist/types/DataTable/ColumnFilters/SortableFilterHeader.d.ts +6 -14
  334. package/dist/types/DataTable/EmptyState.d.ts +6 -9
  335. package/dist/types/DataTable/FilterChips.d.ts +6 -11
  336. package/dist/types/DataTable/GoToPage.d.ts +6 -9
  337. package/dist/types/DataTable/ServerTable.d.ts +16 -80
  338. package/dist/types/DataTable/ServerTableFilterDrawer.d.ts +14 -0
  339. package/dist/types/DataTable/StackedCell.d.ts +12 -0
  340. package/dist/types/DataTable/TableCardsView.d.ts +27 -0
  341. package/dist/types/DataTable/columnWidths.d.ts +21 -0
  342. package/dist/types/DataTable/columns.d.ts +111 -0
  343. package/dist/types/DataTable/filterContext.d.ts +2 -1
  344. package/dist/types/DataTable/fixedColumns.d.ts +29 -0
  345. package/dist/types/DataTable/goToPageOptions.d.ts +15 -0
  346. package/dist/types/DataTable/index.d.ts +7 -1
  347. package/dist/types/DataTable/useTableView.d.ts +22 -0
  348. package/dist/types/access/PermissionMatrix.d.ts +6 -10
  349. package/dist/types/account/AccountSecuritySection.d.ts +6 -7
  350. package/dist/types/account/DemoTotpCodeButton.d.ts +6 -5
  351. package/dist/types/account/ProfileScreen.d.ts +6 -18
  352. package/dist/types/account/SignInScreen.d.ts +6 -13
  353. package/dist/types/appShell/AccountMenu.d.ts +6 -11
  354. package/dist/types/appShell/AppLayout.d.ts +3 -40
  355. package/dist/types/appShell/AppearanceSettings.d.ts +6 -12
  356. package/dist/types/appShell/AuthLayout.d.ts +3 -11
  357. package/dist/types/appShell/BarActionButton.d.ts +6 -10
  358. package/dist/types/appShell/BarActions.d.ts +6 -15
  359. package/dist/types/appShell/BarCapsule.d.ts +6 -9
  360. package/dist/types/appShell/BarSearch.d.ts +6 -15
  361. package/dist/types/appShell/HeaderTools.d.ts +6 -16
  362. package/dist/types/appShell/MobileTabBar.d.ts +6 -13
  363. package/dist/types/appShell/SettingsMenu.d.ts +6 -10
  364. package/dist/types/appShell/SideMenu.d.ts +6 -20
  365. package/dist/types/appShell/SideMenuEntry.d.ts +6 -11
  366. package/dist/types/appShell/SubmenuActionsMenu.d.ts +6 -12
  367. package/dist/types/appShell/TabBarEditor.d.ts +6 -9
  368. package/dist/types/appShell/TopMenu.d.ts +6 -5
  369. package/dist/types/appShell/Wordmark.d.ts +6 -7
  370. package/dist/types/auth/PasswordChecklist.d.ts +6 -9
  371. package/dist/types/calendar/EventCalendar.d.ts +6 -23
  372. package/dist/types/calendar/EventDetails.d.ts +6 -10
  373. package/dist/types/date-pickers.d.ts +10 -1
  374. package/dist/types/datePickers/DateInput.d.ts +10 -23
  375. package/dist/types/datePickers/DateTimeInput.d.ts +30 -0
  376. package/dist/types/datePickers/FormDateTimeField.d.ts +7 -0
  377. package/dist/types/datePickers/FormMonthField.d.ts +7 -0
  378. package/dist/types/datePickers/FormTimeField.d.ts +7 -0
  379. package/dist/types/datePickers/MonthInput.d.ts +25 -0
  380. package/dist/types/datePickers/PickerFrame.d.ts +22 -0
  381. package/dist/types/datePickers/TimeInput.d.ts +24 -0
  382. package/dist/types/datePickers/dateTimeRules.d.ts +31 -0
  383. package/dist/types/datePickers/pickerValues.d.ts +114 -0
  384. package/dist/types/datePickers/useCurrentMinute.d.ts +5 -0
  385. package/dist/types/datePickers/usePickerField.d.ts +28 -0
  386. package/dist/types/dictionaries/dictionaries.d.ts +6 -3
  387. package/dist/types/downloads/DownloadManager.d.ts +6 -6
  388. package/dist/types/feedback/ConfirmDialog.d.ts +6 -18
  389. package/dist/types/feedback/ModalWaiting.d.ts +6 -9
  390. package/dist/types/feedback/NewBuildNotice.d.ts +6 -10
  391. package/dist/types/filepond/FileDropzone.d.ts +6 -29
  392. package/dist/types/forms/FormAttachmentsField.d.ts +7 -0
  393. package/dist/types/forms/FormCheckboxGroupField.d.ts +7 -0
  394. package/dist/types/forms/FormFileField.d.ts +7 -0
  395. package/dist/types/forms/FormFileUploadField.d.ts +7 -0
  396. package/dist/types/forms/FormImageField.d.ts +7 -0
  397. package/dist/types/forms/FormMaskedField.d.ts +7 -0
  398. package/dist/types/forms/FormProblemAlert.d.ts +6 -5
  399. package/dist/types/forms/FormRepeatingFields.d.ts +25 -0
  400. package/dist/types/forms/FormTransferListField.d.ts +7 -0
  401. package/dist/types/forms/SubmitButton.d.ts +6 -6
  402. package/dist/types/forms/auth/SignInForm.d.ts +6 -11
  403. package/dist/types/forms/fieldRules.d.ts +44 -0
  404. package/dist/types/forms/index.d.ts +11 -1
  405. package/dist/types/forms/listRules.d.ts +48 -0
  406. package/dist/types/forms/security/SecondStepForm.d.ts +6 -14
  407. package/dist/types/forms/security/TwoStepVerificationCard.d.ts +6 -21
  408. package/dist/types/forms/serverProblemFields.d.ts +13 -0
  409. package/dist/types/forms/useValidationText.d.ts +12 -0
  410. package/dist/types/forms/validation.d.ts +117 -174
  411. package/dist/types/forms/validationMessages.d.ts +54 -6
  412. package/dist/types/forms.d.ts +1 -1
  413. package/dist/types/hookForm/FormAttachmentsField.d.ts +8 -0
  414. package/dist/types/hookForm/FormCheckboxGroupField.d.ts +8 -0
  415. package/dist/types/hookForm/FormFileField.d.ts +8 -0
  416. package/dist/types/hookForm/FormFileUploadField.d.ts +8 -0
  417. package/dist/types/hookForm/FormImageField.d.ts +8 -0
  418. package/dist/types/hookForm/FormMaskedField.d.ts +9 -0
  419. package/dist/types/hookForm/FormProblemAlert.d.ts +6 -5
  420. package/dist/types/hookForm/FormRepeatingFields.d.ts +27 -0
  421. package/dist/types/hookForm/FormTransferListField.d.ts +8 -0
  422. package/dist/types/hookForm/SubmitButton.d.ts +6 -13
  423. package/dist/types/inbox/InboxLayout.d.ts +6 -17
  424. package/dist/types/inbox/MessageList.d.ts +6 -18
  425. package/dist/types/inbox/MessageView.d.ts +6 -14
  426. package/dist/types/index.d.ts +33 -4
  427. package/dist/types/inputs/CatalogInput.d.ts +10 -21
  428. package/dist/types/inputs/CheckboxGroupInput.d.ts +28 -0
  429. package/dist/types/inputs/ColorInput.d.ts +7 -16
  430. package/dist/types/inputs/FieldFrame.d.ts +6 -10
  431. package/dist/types/inputs/MaskedInput.d.ts +38 -0
  432. package/dist/types/inputs/NumberInput.d.ts +7 -20
  433. package/dist/types/inputs/RepeatingFields.d.ts +31 -0
  434. package/dist/types/inputs/SelectInput.d.ts +10 -18
  435. package/dist/types/inputs/TextInput.d.ts +3 -0
  436. package/dist/types/inputs/TransferListInput.d.ts +30 -0
  437. package/dist/types/inputs/WizardLayout.d.ts +6 -19
  438. package/dist/types/inputs/inputMasks.d.ts +52 -0
  439. package/dist/types/inputs/selectAllState.d.ts +11 -0
  440. package/dist/types/inputs/useRowKeys.d.ts +11 -0
  441. package/dist/types/kanban/KanbanBoard.d.ts +6 -25
  442. package/dist/types/legal/CookieNotice.d.ts +20 -0
  443. package/dist/types/legal/TermsAcceptanceDialog.d.ts +20 -0
  444. package/dist/types/legal/consentStorage.d.ts +16 -0
  445. package/dist/types/legal/useCookieConsent.d.ts +13 -0
  446. package/dist/types/locales/en.d.ts +256 -0
  447. package/dist/types/locales/es.d.ts +256 -0
  448. package/dist/types/maps/MapView.d.ts +6 -17
  449. package/dist/types/mexico/CfdiSummary.d.ts +12 -0
  450. package/dist/types/mexico/EfirmaFields.d.ts +28 -0
  451. package/dist/types/mexico/certificateReader.d.ts +37 -0
  452. package/dist/types/mexico/cfdiReader.d.ts +95 -0
  453. package/dist/types/mexico/derReader.d.ts +54 -0
  454. package/dist/types/mexico/efirmaKey.d.ts +26 -0
  455. package/dist/types/mexico/mexicoIdentifiers.d.ts +68 -0
  456. package/dist/types/mexico/mexicoRules.d.ts +45 -0
  457. package/dist/types/mexico.d.ts +11 -0
  458. package/dist/types/pwa/InstallAppSetting.d.ts +6 -6
  459. package/dist/types/pwa/InstallInstructionsDialog.d.ts +6 -9
  460. package/dist/types/pwa/InstallPromptBanner.d.ts +6 -11
  461. package/dist/types/react-hook-form.d.ts +11 -1
  462. package/dist/types/rich-text.d.ts +5 -0
  463. package/dist/types/richText/FormRichTextField.d.ts +7 -0
  464. package/dist/types/richText/RichTextEditor.d.ts +27 -0
  465. package/dist/types/richText/RichTextLinkPopover.d.ts +17 -0
  466. package/dist/types/richText/RichTextToolbar.d.ts +15 -0
  467. package/dist/types/richText/RichTextView.d.ts +10 -0
  468. package/dist/types/richText/richTextContentSx.d.ts +7 -0
  469. package/dist/types/richText/richTextExtensions.d.ts +15 -0
  470. package/dist/types/richText/richTextRules.d.ts +41 -0
  471. package/dist/types/security/ActivityLog.d.ts +6 -9
  472. package/dist/types/security/RecoveryCodesDialog.d.ts +6 -11
  473. package/dist/types/security/SessionsCard.d.ts +6 -11
  474. package/dist/types/table.d.ts +3 -1
  475. package/dist/types/theme/themePieces.d.ts +55 -3
  476. package/dist/types/toolkit/ToolkitProvider.d.ts +13 -1
  477. package/dist/types/toolkit/browserNavigation.d.ts +7 -3
  478. package/dist/types/toolkit/combineTexts.d.ts +6 -0
  479. package/dist/types/toolkit/hashNavigation.d.ts +4 -2
  480. package/dist/types/toolkit/navigationPaths.d.ts +35 -0
  481. package/dist/types/toolkit/pathIndex.d.ts +27 -0
  482. package/dist/types/toolkit/toolkitSettings.d.ts +7 -4
  483. package/dist/types/ui/actions/ActionsMenu.d.ts +24 -11
  484. package/dist/types/ui/actions/CopyButton.d.ts +8 -11
  485. package/dist/types/ui/actions/DownloadMenu.d.ts +6 -29
  486. package/dist/types/ui/actions/SecretField.d.ts +6 -11
  487. package/dist/types/ui/actions/SelectionActions.d.ts +6 -13
  488. package/dist/types/ui/actions/tableRowText.d.ts +6 -0
  489. package/dist/types/ui/cards/RecordCard.d.ts +40 -0
  490. package/dist/types/ui/cards/RecordCardSkeleton.d.ts +12 -0
  491. package/dist/types/ui/countdown/Countdown.d.ts +22 -0
  492. package/dist/types/ui/countdown/countdownParts.d.ts +39 -0
  493. package/dist/types/ui/countdown/useCountdown.d.ts +14 -0
  494. package/dist/types/ui/documents/PdfViewerDialog.d.ts +14 -0
  495. package/dist/types/ui/filters/DateRangeFilter.d.ts +6 -10
  496. package/dist/types/ui/filters/FilterDrawer.d.ts +23 -0
  497. package/dist/types/ui/filters/PeriodRangeSelector.d.ts +6 -13
  498. package/dist/types/ui/filters/QuickFilters.d.ts +6 -12
  499. package/dist/types/ui/filters/filterDrawerFields.d.ts +139 -0
  500. package/dist/types/ui/help/HelpButton.d.ts +11 -0
  501. package/dist/types/ui/help/HelpCarouselDialog.d.ts +27 -0
  502. package/dist/types/ui/help/useHelpCarousel.d.ts +21 -0
  503. package/dist/types/ui/index.d.ts +10 -0
  504. package/dist/types/ui/labels/IconBadge.d.ts +34 -0
  505. package/dist/types/ui/labels/MarkedText.d.ts +10 -0
  506. package/dist/types/ui/labels/Tag.d.ts +6 -12
  507. package/dist/types/ui/labels/markedTextPieces.d.ts +22 -0
  508. package/dist/types/ui/layout/CollapsibleSection.d.ts +6 -12
  509. package/dist/types/ui/layout/ContentDialog.d.ts +6 -10
  510. package/dist/types/ui/layout/DefinitionList.d.ts +6 -9
  511. package/dist/types/ui/layout/DetailHeader.d.ts +6 -10
  512. package/dist/types/ui/layout/ErrorPage.d.ts +6 -17
  513. package/dist/types/ui/layout/FullPageLoader.d.ts +6 -6
  514. package/dist/types/ui/layout/GlassPanel.d.ts +6 -8
  515. package/dist/types/ui/layout/PageHeader.d.ts +6 -10
  516. package/dist/types/ui/layout/PageHeading.d.ts +6 -11
  517. package/dist/types/ui/layout/ProfileHeader.d.ts +6 -11
  518. package/dist/types/ui/layout/ScrollableRow.d.ts +6 -13
  519. package/dist/types/ui/layout/SectionCard.d.ts +6 -8
  520. package/dist/types/ui/metrics/BarList.d.ts +6 -12
  521. package/dist/types/ui/metrics/CalendarHeatmap.d.ts +6 -19
  522. package/dist/types/ui/metrics/ChartTooltip.d.ts +6 -6
  523. package/dist/types/ui/metrics/ColumnChart.d.ts +6 -25
  524. package/dist/types/ui/metrics/LineChart.d.ts +6 -20
  525. package/dist/types/ui/metrics/MetricCard.d.ts +6 -17
  526. package/dist/types/ui/metrics/TableToggle.d.ts +6 -6
  527. package/dist/types/ui/metrics/chartParts.d.ts +15 -30
  528. package/dist/types/ui/navigation/BackButton.d.ts +6 -9
  529. package/dist/types/ui/navigation/ColorSchemeToggle.d.ts +6 -9
  530. package/dist/types/ui/navigation/FontSizeToggle.d.ts +6 -8
  531. package/dist/types/ui/navigation/LanguageToggle.d.ts +6 -8
  532. package/dist/types/ui/navigation/PageBreadcrumbs.d.ts +6 -7
  533. package/dist/types/ui/navigation/RouteTabs.d.ts +39 -0
  534. package/dist/types/ui/navigation/routeTabMatching.d.ts +27 -0
  535. package/dist/types/ui/timeline/GroupedTimeline.d.ts +6 -23
  536. package/dist/types/upload/AttachmentsInput.d.ts +72 -0
  537. package/dist/types/upload/FileChip.d.ts +18 -0
  538. package/dist/types/upload/FileDropzone.d.ts +7 -19
  539. package/dist/types/upload/FileInput.d.ts +31 -0
  540. package/dist/types/upload/FileUploadInput.d.ts +56 -0
  541. package/dist/types/upload/ImageInput.d.ts +35 -0
  542. package/dist/types/upload/TemplateDownloadButton.d.ts +27 -0
  543. package/dist/types/upload/fileAppearance.d.ts +8 -0
  544. package/dist/types/upload/fileRules.d.ts +38 -0
  545. package/dist/types/upload/transferProgress.d.ts +18 -0
  546. package/dist/types/upload/uploadMachine.d.ts +6 -2
  547. package/dist/types/upload/uploadWithProgress.d.ts +6 -2
  548. package/dist/types/upload/useFilePicker.d.ts +39 -0
  549. package/dist/ui/actions/ActionsMenu.js +109 -17
  550. package/dist/ui/actions/ActionsMenu.js.map +1 -1
  551. package/dist/ui/actions/CopyButton.js +37 -12
  552. package/dist/ui/actions/CopyButton.js.map +1 -1
  553. package/dist/ui/actions/DownloadMenu.js +3 -2
  554. package/dist/ui/actions/DownloadMenu.js.map +1 -1
  555. package/dist/ui/actions/SecretField.js +8 -3
  556. package/dist/ui/actions/SecretField.js.map +1 -1
  557. package/dist/ui/actions/SelectionActions.js +3 -2
  558. package/dist/ui/actions/SelectionActions.js.map +1 -1
  559. package/dist/ui/actions/tableRowText.js +7 -0
  560. package/dist/ui/actions/tableRowText.js.map +1 -0
  561. package/dist/ui/cards/RecordCard.js +146 -0
  562. package/dist/ui/cards/RecordCard.js.map +1 -0
  563. package/dist/ui/cards/RecordCardSkeleton.js +38 -0
  564. package/dist/ui/cards/RecordCardSkeleton.js.map +1 -0
  565. package/dist/ui/countdown/Countdown.js +39 -0
  566. package/dist/ui/countdown/Countdown.js.map +1 -0
  567. package/dist/ui/countdown/countdownParts.js +34 -0
  568. package/dist/ui/countdown/countdownParts.js.map +1 -0
  569. package/dist/ui/countdown/useCountdown.js +35 -0
  570. package/dist/ui/countdown/useCountdown.js.map +1 -0
  571. package/dist/ui/documents/PdfViewerDialog.js +82 -0
  572. package/dist/ui/documents/PdfViewerDialog.js.map +1 -0
  573. package/dist/ui/filters/DateRangeFilter.js +3 -2
  574. package/dist/ui/filters/DateRangeFilter.js.map +1 -1
  575. package/dist/ui/filters/FilterDrawer.js +212 -0
  576. package/dist/ui/filters/FilterDrawer.js.map +1 -0
  577. package/dist/ui/filters/PeriodRangeSelector.js +3 -2
  578. package/dist/ui/filters/PeriodRangeSelector.js.map +1 -1
  579. package/dist/ui/filters/QuickFilters.js +3 -2
  580. package/dist/ui/filters/QuickFilters.js.map +1 -1
  581. package/dist/ui/filters/filterDrawerFields.js +119 -0
  582. package/dist/ui/filters/filterDrawerFields.js.map +1 -0
  583. package/dist/ui/help/HelpButton.js +22 -0
  584. package/dist/ui/help/HelpButton.js.map +1 -0
  585. package/dist/ui/help/HelpCarouselDialog.js +155 -0
  586. package/dist/ui/help/HelpCarouselDialog.js.map +1 -0
  587. package/dist/ui/help/useHelpCarousel.js +25 -0
  588. package/dist/ui/help/useHelpCarousel.js.map +1 -0
  589. package/dist/ui/labels/IconBadge.js +82 -0
  590. package/dist/ui/labels/IconBadge.js.map +1 -0
  591. package/dist/ui/labels/MarkedText.js +31 -0
  592. package/dist/ui/labels/MarkedText.js.map +1 -0
  593. package/dist/ui/labels/Tag.js +3 -2
  594. package/dist/ui/labels/Tag.js.map +1 -1
  595. package/dist/ui/labels/markedTextPieces.js +64 -0
  596. package/dist/ui/labels/markedTextPieces.js.map +1 -0
  597. package/dist/ui/layout/CollapsibleSection.js +3 -2
  598. package/dist/ui/layout/CollapsibleSection.js.map +1 -1
  599. package/dist/ui/layout/ContentDialog.js +3 -2
  600. package/dist/ui/layout/ContentDialog.js.map +1 -1
  601. package/dist/ui/layout/DefinitionList.js +3 -2
  602. package/dist/ui/layout/DefinitionList.js.map +1 -1
  603. package/dist/ui/layout/DetailHeader.js +3 -2
  604. package/dist/ui/layout/DetailHeader.js.map +1 -1
  605. package/dist/ui/layout/ErrorPage.js +3 -2
  606. package/dist/ui/layout/ErrorPage.js.map +1 -1
  607. package/dist/ui/layout/FullPageLoader.js +3 -2
  608. package/dist/ui/layout/FullPageLoader.js.map +1 -1
  609. package/dist/ui/layout/GlassPanel.js +3 -2
  610. package/dist/ui/layout/GlassPanel.js.map +1 -1
  611. package/dist/ui/layout/PageHeader.js +3 -2
  612. package/dist/ui/layout/PageHeader.js.map +1 -1
  613. package/dist/ui/layout/PageHeading.js +3 -2
  614. package/dist/ui/layout/PageHeading.js.map +1 -1
  615. package/dist/ui/layout/ProfileHeader.js +3 -2
  616. package/dist/ui/layout/ProfileHeader.js.map +1 -1
  617. package/dist/ui/layout/ScrollableRow.js +3 -2
  618. package/dist/ui/layout/ScrollableRow.js.map +1 -1
  619. package/dist/ui/layout/SectionCard.js +3 -2
  620. package/dist/ui/layout/SectionCard.js.map +1 -1
  621. package/dist/ui/metrics/BarList.js +3 -2
  622. package/dist/ui/metrics/BarList.js.map +1 -1
  623. package/dist/ui/metrics/CalendarHeatmap.js +3 -2
  624. package/dist/ui/metrics/CalendarHeatmap.js.map +1 -1
  625. package/dist/ui/metrics/ChartTooltip.js +3 -2
  626. package/dist/ui/metrics/ChartTooltip.js.map +1 -1
  627. package/dist/ui/metrics/ColumnChart.js +3 -2
  628. package/dist/ui/metrics/ColumnChart.js.map +1 -1
  629. package/dist/ui/metrics/LineChart.js +3 -2
  630. package/dist/ui/metrics/LineChart.js.map +1 -1
  631. package/dist/ui/metrics/MetricCard.js +3 -2
  632. package/dist/ui/metrics/MetricCard.js.map +1 -1
  633. package/dist/ui/metrics/TableToggle.js +3 -2
  634. package/dist/ui/metrics/TableToggle.js.map +1 -1
  635. package/dist/ui/metrics/chartParts.js +7 -4
  636. package/dist/ui/metrics/chartParts.js.map +1 -1
  637. package/dist/ui/navigation/BackButton.js +7 -2
  638. package/dist/ui/navigation/BackButton.js.map +1 -1
  639. package/dist/ui/navigation/ColorSchemeToggle.js +3 -2
  640. package/dist/ui/navigation/ColorSchemeToggle.js.map +1 -1
  641. package/dist/ui/navigation/FontSizeToggle.js +3 -2
  642. package/dist/ui/navigation/FontSizeToggle.js.map +1 -1
  643. package/dist/ui/navigation/LanguageToggle.js +3 -2
  644. package/dist/ui/navigation/LanguageToggle.js.map +1 -1
  645. package/dist/ui/navigation/PageBreadcrumbs.js +3 -2
  646. package/dist/ui/navigation/PageBreadcrumbs.js.map +1 -1
  647. package/dist/ui/navigation/RouteTabs.js +79 -0
  648. package/dist/ui/navigation/RouteTabs.js.map +1 -0
  649. package/dist/ui/navigation/routeTabMatching.js +23 -0
  650. package/dist/ui/navigation/routeTabMatching.js.map +1 -0
  651. package/dist/ui/timeline/GroupedTimeline.js +3 -2
  652. package/dist/ui/timeline/GroupedTimeline.js.map +1 -1
  653. package/dist/upload/AttachmentsInput.js +130 -0
  654. package/dist/upload/AttachmentsInput.js.map +1 -0
  655. package/dist/upload/FileChip.js +28 -0
  656. package/dist/upload/FileChip.js.map +1 -0
  657. package/dist/upload/FileDropzone.js +23 -87
  658. package/dist/upload/FileDropzone.js.map +1 -1
  659. package/dist/upload/FileInput.js +115 -0
  660. package/dist/upload/FileInput.js.map +1 -0
  661. package/dist/upload/FileUploadInput.js +288 -0
  662. package/dist/upload/FileUploadInput.js.map +1 -0
  663. package/dist/upload/ImageInput.js +128 -0
  664. package/dist/upload/ImageInput.js.map +1 -0
  665. package/dist/upload/TemplateDownloadButton.js +68 -0
  666. package/dist/upload/TemplateDownloadButton.js.map +1 -0
  667. package/dist/upload/fileAppearance.js +21 -0
  668. package/dist/upload/fileAppearance.js.map +1 -0
  669. package/dist/upload/fileRules.js +27 -1
  670. package/dist/upload/fileRules.js.map +1 -1
  671. package/dist/upload/transferProgress.js +23 -0
  672. package/dist/upload/transferProgress.js.map +1 -0
  673. package/dist/upload/uploadMachine.js +8 -8
  674. package/dist/upload/uploadMachine.js.map +1 -1
  675. package/dist/upload/uploadWithProgress.js +1 -1
  676. package/dist/upload/uploadWithProgress.js.map +1 -1
  677. package/dist/upload/useFilePicker.js +101 -0
  678. package/dist/upload/useFilePicker.js.map +1 -0
  679. package/package.json +44 -1
  680. package/cli/templates/project/src/app/combineTexts.js.tpl +0 -31
@@ -0,0 +1,209 @@
1
+ // calcita check:texts — revisa los textos del proyecto sin abrir la app:
2
+ // 1. que cada t('llave') escrita tal cual en el código exista en todos los idiomas (con su namespace: el de
3
+ // useToolkitTranslation('orders') del archivo, o el que trae la llave, 'common:actions.save');
4
+ // 2. que cada idioma tenga las mismas llaves que los demás (lo que falta en inglés se pintaría en español… o como llave).
5
+ // Sin esto, una llave que no existe se descubre cuando alguien la ve en la pantalla («profile:tabs.documents»).
6
+ //
7
+ // Lo que NO revisa: las llaves armadas con una variable (t(`status.${value}`)) ni las que llevan defaultValue (son opcionales).
8
+ // Para esas está el aviso de Calcita en la consola (onMissingText de ToolkitProvider).
9
+ //
10
+ // Lee los archivos de textos del proyecto (src/**/*Texts.js y src/**/texts.js: cada uno exporta {es: {...}, en: {...}}) con
11
+ // node, así que no deben importar otros archivos; el que los junta (con import.meta.glob) se salta. Los namespaces de Calcita
12
+ // (common, table, validation…) salen de su dist/.
13
+ //
14
+ // Solo node, sin dependencias (el CLI corre antes de que el proyecto tenga nada instalado): function (regla de la casa).
15
+ // Pruebas en checkTexts.test.js.
16
+ import {readdir, readFile} from 'node:fs/promises';
17
+ import {join, relative} from 'node:path';
18
+ import {pathToFileURL} from 'node:url';
19
+
20
+ const SOURCE_EXTENSIONS = /\.(jsx?|tsx?)$/;
21
+ const TEST_FILE = /\.test\.(jsx?|tsx?)$/;
22
+ const TEXT_FILE = /(?:^|\/)(?:texts|[A-Za-z0-9]+Texts)\.js$/;
23
+ const IGNORED_FOLDERS = new Set(['node_modules', 'dist', 'coverage', '.git']);
24
+ // el namespace de un archivo: useToolkitTranslation('orders') o useToolkitTranslation(['orders', …])
25
+ const NAMESPACE_CALL = /useToolkitTranslation\(\s*\[?\s*(['"])([\w-]+)\1/g;
26
+ // una llave escrita tal cual: t('list.title') o t("common:actions.save"); con lo que sigue, para ver si trae defaultValue
27
+ const KEY_CALL = /\bt\(\s*(['"])([^'"`$\n]+?)\1([^)]{0,200})/g;
28
+ const PLURAL_SUFFIXES = ['', '_zero', '_one', '_two', '_few', '_many', '_other'];
29
+
30
+ /**
31
+ * Los archivos de una carpeta y sus subcarpetas, sin node_modules ni dist.
32
+ *
33
+ * @param {string} folder
34
+ * @returns {Promise<string[]>}
35
+ */
36
+ async function filesOf(folder) {
37
+ const entries = await readdir(folder, {withFileTypes: true}).catch(() => []);
38
+ const nested = await Promise.all(entries.map(async (entry) => {
39
+ const path = join(folder, entry.name);
40
+ if (entry.isDirectory()) {
41
+ return IGNORED_FOLDERS.has(entry.name) ? [] : filesOf(path);
42
+ }
43
+ return [path];
44
+ }));
45
+ return nested.flat();
46
+ }
47
+
48
+ /**
49
+ * Suma los textos de un archivo a los de todos (por idioma y namespace, objetos por dentro). Aquí no truena con un repetido:
50
+ * lo anota, para decirlo junto con lo demás.
51
+ */
52
+ function addTexts(combined, fileTexts, duplicates, fileName) {
53
+ const mergeInto = (target, added, place) => {
54
+ for (const [key, value] of Object.entries(added)) {
55
+ const isObject = value && typeof value === 'object';
56
+ const keyPath = [...place.path, key];
57
+ if (isObject && target[key] && typeof target[key] === 'object') {
58
+ mergeInto(target[key], value, {...place, path: keyPath});
59
+ } else if (Object.hasOwn(target, key)) {
60
+ duplicates.push(`«${place.namespace}:${keyPath.join('.')}» en ${place.language} (otra vez en ${fileName})`);
61
+ } else {
62
+ target[key] = isObject ? structuredClone(value) : value;
63
+ }
64
+ }
65
+ };
66
+ for (const [language, namespaces] of Object.entries(fileTexts)) {
67
+ combined[language] ??= {};
68
+ for (const [namespace, keys] of Object.entries(namespaces ?? {})) {
69
+ combined[language][namespace] ??= {};
70
+ mergeInto(combined[language][namespace], keys, {language, namespace, path: []});
71
+ }
72
+ }
73
+ }
74
+
75
+ /**
76
+ * ¿Parece un objeto de textos? ({es: {namespace: {...}}, en: {...}}: llaves de idioma con objetos adentro)
77
+ *
78
+ * @param {unknown} value
79
+ * @returns {boolean}
80
+ */
81
+ function looksLikeTexts(value) {
82
+ return Boolean(value) && typeof value === 'object' && Object.keys(value).length > 0
83
+ && Object.entries(value).every(([language, namespaces]) => /^[a-z]{2}(-[A-Z]{2})?$/.test(language) && namespaces && typeof namespaces === 'object');
84
+ }
85
+
86
+ /** Lo que hay en un camino con puntos ('list.title') dentro de un objeto. */
87
+ function valueAt(object, dottedKey) {
88
+ return dottedKey.split('.').reduce((current, step) => (current && typeof current === 'object' ? current[step] : undefined), object);
89
+ }
90
+
91
+ /** ¿Existe la llave (o su plural) en ese namespace? */
92
+ function hasKey(namespaceTexts, key) {
93
+ return PLURAL_SUFFIXES.some((suffix) => typeof valueAt(namespaceTexts, `${key}${suffix}`) === 'string');
94
+ }
95
+
96
+ /** Todas las llaves con texto de un objeto, con puntos (para comparar idiomas); los plurales cuentan como su llave base. */
97
+ function leafKeysOf(object, prefix = '') {
98
+ return Object.entries(object ?? {}).flatMap(([key, value]) => {
99
+ const path = prefix ? `${prefix}.${key}` : key;
100
+ if (value && typeof value === 'object') {
101
+ return leafKeysOf(value, path);
102
+ }
103
+ return [path.replace(/_(zero|one|two|few|many|other)$/, '')];
104
+ });
105
+ }
106
+
107
+ /**
108
+ * Los textos de Calcita (common, table, validation…), del dist/ del paquete que corre este CLI. Sin dist/ (el repo del paquete
109
+ * sin construir), ninguno: las llaves de esos namespaces no se revisan.
110
+ *
111
+ * @returns {Promise<Record<string, object>>}
112
+ */
113
+ async function packageTextsOf() {
114
+ const texts = {};
115
+ for (const language of ['es', 'en']) {
116
+ try {
117
+ const module = await import(new URL(`../dist/locales/${language}.js`, import.meta.url).href);
118
+ texts[language] = module.default ?? {};
119
+ } catch {
120
+ return {};
121
+ }
122
+ }
123
+ return texts;
124
+ }
125
+
126
+ /**
127
+ * @param {object} options
128
+ * @param {string} options.projectRoot la carpeta del proyecto
129
+ * @param {string} [options.sourceFolder] dónde está el código (por omisión src)
130
+ * @param {Record<string, object>} [options.packageTexts] los textos de Calcita (por omisión, los de su dist/)
131
+ * @returns {Promise<{problems: string[], checkedKeys: number, textFiles: string[], languages: string[]}>}
132
+ */
133
+ export async function checkTexts({projectRoot, sourceFolder = 'src', packageTexts}) {
134
+ const sourceRoot = join(projectRoot, sourceFolder);
135
+ const files = await filesOf(sourceRoot);
136
+ const problems = [];
137
+
138
+ // 1. los textos del proyecto
139
+ const projectTexts = {};
140
+ const duplicates = [];
141
+ const textFiles = files.filter((file) => TEXT_FILE.test(file.replaceAll('\\', '/')));
142
+ const readTextFiles = [];
143
+ for (const file of textFiles) {
144
+ const fileName = relative(projectRoot, file);
145
+ // el que JUNTA los textos (appTexts.js, con el glob de Vite) no es un archivo de textos: se salta
146
+ if ((await readFile(file, 'utf8')).includes('import.meta.glob')) {
147
+ continue;
148
+ }
149
+ readTextFiles.push(fileName);
150
+ try {
151
+ const module = await import(pathToFileURL(file).href);
152
+ const exported = Object.values(module).filter(looksLikeTexts);
153
+ if (exported.length === 0) {
154
+ problems.push(`${fileName}: no exporta textos con la forma {es: {...}, en: {...}}`);
155
+ }
156
+ exported.forEach((fileTexts) => addTexts(projectTexts, fileTexts, duplicates, fileName));
157
+ } catch (error) {
158
+ problems.push(`${fileName}: no se pudo leer (${error.message.split('\n')[0]}); un archivo de textos no debe importar otros archivos`);
159
+ }
160
+ }
161
+ problems.push(...duplicates.map((duplicate) => `texto repetido: ${duplicate}`));
162
+
163
+ const languages = Object.keys(projectTexts);
164
+ // 2. que cada idioma tenga las mismas llaves
165
+ for (const language of languages) {
166
+ for (const otherLanguage of languages.filter((other) => other !== language)) {
167
+ for (const namespace of Object.keys(projectTexts[language])) {
168
+ const otherKeys = new Set(leafKeysOf(projectTexts[otherLanguage][namespace]));
169
+ for (const key of new Set(leafKeysOf(projectTexts[language][namespace]))) {
170
+ if (!otherKeys.has(key)) {
171
+ problems.push(`«${namespace}:${key}» está en ${language} y falta en ${otherLanguage}`);
172
+ }
173
+ }
174
+ }
175
+ }
176
+ }
177
+
178
+ // 3. cada t('llave') escrita tal cual
179
+ const calcitaTexts = packageTexts ?? await packageTextsOf();
180
+ const textsOf = (language, namespace) => projectTexts[language]?.[namespace] ?? calcitaTexts[language]?.[namespace];
181
+ let checkedKeys = 0;
182
+ for (const file of files.filter((path) => SOURCE_EXTENSIONS.test(path) && !TEST_FILE.test(path))) {
183
+ const code = await readFile(file, 'utf8');
184
+ const fileNamespaces = [...code.matchAll(NAMESPACE_CALL)].map((match) => match[2]);
185
+ for (const match of code.matchAll(KEY_CALL)) {
186
+ const [, , key, rest] = match;
187
+ // con defaultValue la llave es opcional; una llave con espacios es un texto, no una llave
188
+ if (/defaultValue/.test(rest) || /\s/.test(key)) {
189
+ continue;
190
+ }
191
+ const line = code.slice(0, match.index).split('\n').length;
192
+ const place = `${relative(projectRoot, file)}:${line}`;
193
+ const [explicitNamespace, plainKey] = key.includes(':') ? key.split(':', 2) : [null, key];
194
+ const candidates = explicitNamespace ? [explicitNamespace] : fileNamespaces;
195
+ if (candidates.length === 0) {
196
+ continue;
197
+ }
198
+ checkedKeys += 1;
199
+ for (const language of languages.length > 0 ? languages : ['es']) {
200
+ const found = candidates.some((namespace) => hasKey(textsOf(language, namespace), plainKey));
201
+ if (!found) {
202
+ problems.push(`${place} «${explicitNamespace ? key : `${candidates[0]}:${key}`}» no existe en ${language}`);
203
+ }
204
+ }
205
+ }
206
+ }
207
+
208
+ return {problems, checkedKeys, textFiles: readTextFiles, languages};
209
+ }
@@ -5,9 +5,23 @@
5
5
  import {readFile} from 'node:fs/promises';
6
6
 
7
7
  // Lo que el paquete no usa, así que no está en sus devDependencies: i18next (su adaptador recibe la instancia del proyecto;
8
- // versión con más de dos semanas publicada al escogerla, pnpm espera un día antes de instalar una recién salida) y qrcode
9
- // (el QR de la cuenta de demostración; la misma versión que usan los ejemplos)
10
- const VERSIONS_NOT_IN_PACKAGE = Object.freeze({i18next: '26.4.2', qrcode: '1.5.4'});
8
+ // versión con más de dos semanas publicada al escogerla, pnpm espera un día antes de instalar una recién salida), qrcode
9
+ // (el QR de la cuenta de demostración; la misma versión que usan los ejemplos) y las pruebas de pantalla del proyecto
10
+ // (Vitest con jsdom y Testing Library), en las versiones que ya se probaron juntas con el mismo Vite 7 y React 19, y ESLint
11
+ const VERSIONS_NOT_IN_PACKAGE = Object.freeze({
12
+ i18next: '26.4.2',
13
+ qrcode: '1.5.4',
14
+ vitest: '5.0.3',
15
+ jsdom: '30.1.2',
16
+ '@testing-library/dom': '10.4.2',
17
+ '@testing-library/react': '16.3.3',
18
+ '@testing-library/user-event': '14.6.7',
19
+ // ESLint del proyecto nuevo (pnpm lint): versiones con más de dos semanas publicadas al escogerlas (03/11/2026)
20
+ eslint: '10.13.0',
21
+ '@eslint/js': '10.0.1',
22
+ 'eslint-plugin-react-hooks': '7.1.1',
23
+ globals: '17.13.0',
24
+ });
11
25
 
12
26
  /**
13
27
  * El package.json de Calcita (el del CLI que corre). OJO: por ruta de archivo y no con import: el CLI se publica tal cual,
@@ -15,7 +15,12 @@ const BASE_DEPENDENCIES = Object.freeze([
15
15
  const HOOK_FORM_DEPENDENCIES = Object.freeze(['@hookform/resolvers', 'react-hook-form']);
16
16
  // la cuenta: las llaves de acceso (las pide @stackcraftmx/calcita/account) y el QR de la demostración
17
17
  const ACCOUNT_DEPENDENCIES = Object.freeze(['@simplewebauthn/browser', 'qrcode']);
18
- const DEV_DEPENDENCIES = Object.freeze(['@vitejs/plugin-react', 'vite']);
18
+ // las pruebas de pantalla (pnpm test): Vitest con jsdom como navegador y Testing Library para picar como una persona; y ESLint
19
+ // (pnpm lint), que caza al escribir lo que si no se ve en la pantalla del cliente (una variable que no existe: «t is not defined»)
20
+ const DEV_DEPENDENCIES = Object.freeze([
21
+ '@eslint/js', 'eslint', 'eslint-plugin-react-hooks', 'globals',
22
+ '@testing-library/dom', '@testing-library/react', '@testing-library/user-event', '@vitejs/plugin-react', 'jsdom', 'vite', 'vitest',
23
+ ]);
19
24
 
20
25
  /** Un objeto con sus llaves en orden alfabético (como los escribe npm). */
21
26
  const sortedByKey = (record) => Object.fromEntries(Object.entries(record).sort(([first], [second]) => first.localeCompare(second)));
@@ -40,7 +45,7 @@ export function packageJsonOf({packageName, answers, calcitaPackage}) {
40
45
  version: '0.1.0',
41
46
  private: true,
42
47
  type: 'module',
43
- scripts: {dev: 'vite', build: 'vite build', preview: 'vite preview'},
48
+ scripts: {dev: 'vite', build: 'vite build', preview: 'vite preview', test: 'vitest run', lint: 'eslint .', 'check:texts': 'calcita check:texts'},
44
49
  dependencies: sortedByKey({'@stackcraftmx/calcita': `^${calcitaPackage.version}`, ...versionsOf(dependencyNames, calcitaPackage)}),
45
50
  devDependencies: sortedByKey(versionsOf([...DEV_DEPENDENCIES], calcitaPackage)),
46
51
  engines: {node: '>=22'},
@@ -0,0 +1,24 @@
1
+ // La prueba de pantalla de __LABEL_ES__ (la armó el CLI de Calcita con make:module): la app completa abre en su ruta, con su
2
+ // servidor de mentira, enseña lo que manda el servidor y agrega uno. Cámbiela junto con la pantalla.
3
+ //
4
+ // pnpm test
5
+ import {screen} from '@testing-library/react';
6
+ import {describe, expect, it} from 'vitest';
7
+
8
+ import {__NAME___PATH} from '@/modules/__name__/routes.js';
9
+ import {openScreen} from '@/testing/openScreen.jsx';
10
+
11
+ describe('__LABEL_ES__', () => {
12
+ it('enseña lo que manda el servidor', async () => {
13
+ await openScreen(__NAME___PATH);
14
+ expect(await screen.findByRole('heading', {name: '__LABEL_ES__'})).toBeTruthy();
15
+ expect(await screen.findByText('Primer ejemplo')).toBeTruthy();
16
+ });
17
+
18
+ it('agrega uno y sale en la lista', async () => {
19
+ const {user} = await openScreen(__NAME___PATH);
20
+ await user.type(await screen.findByRole('textbox', {name: 'Nombre'}), 'Uno nuevo');
21
+ await user.click(screen.getByRole('button', {name: 'Guardar'}));
22
+ expect(await screen.findByText('Uno nuevo')).toBeTruthy();
23
+ });
24
+ });
@@ -31,7 +31,8 @@ src/
31
31
  app/ el único lugar donde se conecta el framework: proveedores, marco, rutas, tema y textos
32
32
  core/ lo que comparten dos o más módulos (se importa de '@/core')
33
33
  fakeServer/ el servidor de mentira: datos en memoria con la forma del API de verdad
34
- modules/<módulo>/ cada módulo en su carpeta: menu.js, routes.js, sus pantallas, su *Api.js y sus *Texts.js
34
+ modules/<módulo>/ cada módulo en su carpeta: menu.js, routes.js, sus pantallas, su *Api.js, sus *Texts.js y sus pruebas
35
+ testing/ el kit de las pruebas de pantalla: abrir la app en una dirección (openScreen) y los huecos de jsdom
35
36
  ```
36
37
 
37
38
  - **Un módulo nuevo no edita nada fuera de su carpeta**: el menú, las rutas y los textos se descubren solos.
@@ -42,6 +43,29 @@ src/
42
43
  validaciones) ya vienen.
43
44
  - **Imports con el alias `@/`** (= `src/`), declarado en `vite.config.js` y en `jsconfig.json`.
44
45
 
46
+ ## Pruebas de pantalla
47
+
48
+ ```bash
49
+ pnpm test
50
+ ```
51
+
52
+ Cada `*.test.jsx` abre la app COMPLETA en una dirección (`openScreen`, en `src/testing`), con el servidor de mentira, y pica
53
+ como una persona: busca lo que se ve por su papel y su nombre (`screen.findByRole('heading', {name: 'Bienvenida'})`) y da
54
+ clic o escribe con `user`. Pasa por lo mismo que la persona (permisos, menú, textos, avisos). Vienen las del inicio y las del
55
+ módulo de ejemplo, y `make:module` arma la de cada módulo nuevo. Corren con Vitest y jsdom (un navegador de mentira): lo que
56
+ depende de medir o pintar se revisa en un navegador de verdad.
57
+
58
+ ## Revisar antes de subir
59
+
60
+ ```bash
61
+ pnpm lint # ESLint: una variable que no existe, los hooks de React fuera de orden
62
+ pnpm check:texts # que cada t('llave') exista en todos los idiomas y que los idiomas tengan las mismas llaves
63
+ ```
64
+
65
+ Las dos cazan al escribir lo que si no se descubre en la pantalla del cliente («t is not defined», «orders:tabs.documents»).
66
+ Una llave armada con una variable no se puede revisar así: para esas, Calcita avisa en la consola del navegador (y
67
+ `onMissingText` de `ToolkitProvider` se lo dice a su proyecto).
68
+
45
69
  ## Publicar una versión nueva
46
70
 
47
71
  `pnpm build` deja la app en `dist/`, con `version.json` (la versión de esa compilación). La app publicada lo vuelve a leer al
@@ -68,7 +92,16 @@ location /ventas/ {
68
92
  }
69
93
  ```
70
94
 
71
- Con otro prefijo se vuelve a construir.
95
+ Con otro prefijo se vuelve a construir… o, si el prefijo lo decide el servidor al servir la página (Laravel con
96
+ `config('app.url')`, un proxy con su ruta), se construye una sola vez con archivos relativos y la página trae su `<base>`:
97
+
98
+ ```bash
99
+ ASSET_URL=./ pnpm build
100
+ ```
101
+
102
+ ```html
103
+ <base href="/ventas/"> <!-- lo pone el servidor en index.html; el router y publicUrlOf lo leen al abrir -->
104
+ ```
72
105
 
73
106
  ## Crecer la app con el CLI
74
107
 
@@ -0,0 +1,27 @@
1
+ // Las reglas de ESLint del proyecto (pnpm lint): lo que se caza al escribir y no en la pantalla del cliente. La que más
2
+ // ahorra: no-undef, una variable que no existe (un `t` que una función auxiliar se quedó sin recibir truena con «t is not
3
+ // defined» hasta que alguien abre esa pantalla). Y las de los hooks de React (en el orden de siempre, con sus dependencias).
4
+ import js from '@eslint/js';
5
+ import reactHooks from 'eslint-plugin-react-hooks';
6
+ import globals from 'globals';
7
+
8
+ export default [
9
+ {ignores: ['dist/', 'coverage/']},
10
+ js.configs.recommended,
11
+ reactHooks.configs.flat.recommended,
12
+ {
13
+ files: ['**/*.{js,jsx}'],
14
+ languageOptions: {
15
+ ecmaVersion: 'latest',
16
+ sourceType: 'module',
17
+ globals: {...globals.browser},
18
+ parserOptions: {ecmaFeatures: {jsx: true}},
19
+ },
20
+ rules: {
21
+ // un parámetro que no se usa a propósito lleva «unused» en el nombre (unusedEvent)
22
+ 'no-unused-vars': ['error', {argsIgnorePattern: '^unused', varsIgnorePattern: '^unused', caughtErrors: 'none'}],
23
+ },
24
+ },
25
+ // la configuración y las pruebas corren en node
26
+ {files: ['*.config.js', 'src/testing/**', '**/*.test.{js,jsx}'], languageOptions: {globals: {...globals.node}}},
27
+ ];
@@ -28,7 +28,8 @@ const moduleRouteGroups = Object.entries(ROUTE_MODULES)
28
28
  // las pantallas sin el marco (entrar…)
29
29
  const publicRoutes = Object.values(ROUTE_MODULES).flatMap((routeModule) => routeModule.publicRoutes ?? []);
30
30
 
31
- export const appRouter = createBrowserRouter([
31
+ /** Las rutas de la app: el router del navegador (abajo) y el de memoria de las pruebas de pantalla (src/testing). */
32
+ export const appRoutes = [
32
33
  {
33
34
  path: APP_PATHS.home,
34
35
  element: <AppProviders />,
@@ -46,6 +47,8 @@ export const appRouter = createBrowserRouter([
46
47
  },
47
48
  ]),
48
49
  },
50
+ ];
51
+
49
52
  // detrás de un proxy con prefijo (ASSET_URL), el router lo quita al leer la dirección y lo pone en cada liga: las pantallas
50
53
  // siguen escribiendo sus rutas desde la raíz ('/contacts')
51
- ], {basename: ROUTER_BASENAME});
54
+ export const appRouter = createBrowserRouter(appRoutes, {basename: ROUTER_BASENAME});
@@ -4,7 +4,8 @@
4
4
  //
5
5
  // OJO: constante de módulo. ToolkitProvider vuelve a configurar todo cuando cambian sus textos; un objeto nuevo en cada
6
6
  // pintada lo haría en cada pintada.
7
- import {combineTexts} from '@/app/combineTexts.js';
7
+ // combineTexts es del paquete: un texto repetido entre módulos truena y los objetos de adentro (validation.fields) se suman
8
+ import {combineTexts} from '@stackcraftmx/calcita';
8
9
  import {shellTexts} from '@/app/shellTexts.js';
9
10
 
10
11
  const MODULE_TEXT_FILES = import.meta.glob('@/modules/*/*Texts.js', {eager: true});
@@ -20,6 +20,9 @@ export const DEMO_USER = Object.freeze({
20
20
  id: 'user-demo', name: 'Ana Martínez Ruiz', email: 'ana.martinez@ejemplo.com', subtitle: 'Administración', phone: '',
21
21
  });
22
22
 
23
+ /** Dónde recuerda la demostración la sesión en este navegador (con el nombre de la app, para no chocar con otra). */
24
+ export const DEMO_SESSION_STORAGE_KEY = '__APP_SLUG__:session';
25
+
23
26
  /** Sin dirección del API, la app trabaja con la demostración. */
24
27
  export const USES_DEMO_ACCOUNT = !import.meta.env.VITE_API_URL;
25
28
 
@@ -27,8 +30,7 @@ export const accountConnector = USES_DEMO_ACCOUNT
27
30
  ? createDemoAccountConnector({
28
31
  user: DEMO_USER,
29
32
  labels: ['Administración'],
30
- // la sesión de la demostración se recuerda en este navegador, con el nombre de la app para no chocar con otra
31
- storageKey: '__APP_SLUG__:session',
33
+ storageKey: DEMO_SESSION_STORAGE_KEY,
32
34
  issuer: '__APP_TITLE__',
33
35
  // el QR que escanea la app autenticadora
34
36
  toQrImageUrl: (otpauthUrl) => QRCode.toDataURL(otpauthUrl, {margin: 1}),
@@ -5,13 +5,21 @@
5
5
  // OJO: una dirección escrita a mano desde la raíz ('/favicon.svg', '/version.json') funciona en la raíz y se rompe detrás del
6
6
  // proxy: se arma con publicUrlOf.
7
7
  //
8
+ // El prefijo también puede llegar al SERVIR la página, sin volver a construir: si la página trae <base href="/ventas/"> (Laravel
9
+ // lo pone con config('app.url'), un proxy con su ruta), manda ese (documentBasePath). Para eso se construye con ASSET_URL=./
10
+ // (archivos relativos a la página). Sin <base>, todo sigue como arriba.
11
+ //
8
12
  // Funciones puras, no renderizan: function (regla de la casa).
9
13
 
14
+ import {documentBasePath} from '@stackcraftmx/calcita';
15
+
10
16
  // en las pruebas de node no hay import.meta.env (es de Vite): ahí, la raíz
11
17
  const APP_BASE_URL = import.meta.env?.BASE_URL ?? '/';
18
+ // el prefijo que puso el servidor en <base href> ('' en la raíz), o null si la página no trae <base>
19
+ const SERVED_BASE_PATH = documentBasePath();
12
20
 
13
21
  /** El prefijo como lo pide el router: '/ventas' (sin la diagonal del final) o '/'. */
14
- export const ROUTER_BASENAME = APP_BASE_URL.replace(/\/$/, '') || '/';
22
+ export const ROUTER_BASENAME = (SERVED_BASE_PATH ?? APP_BASE_URL.replace(/\/$/, '')) || '/';
15
23
 
16
24
  /**
17
25
  * La dirección de un archivo de public/ con el prefijo: 'favicon.svg' → '/ventas/favicon.svg'.
@@ -20,5 +28,5 @@ export const ROUTER_BASENAME = APP_BASE_URL.replace(/\/$/, '') || '/';
20
28
  * @returns {string}
21
29
  */
22
30
  export function publicUrlOf(path) {
23
- return `${APP_BASE_URL}${path}`;
31
+ return SERVED_BASE_PATH === null ? `${APP_BASE_URL}${path}` : `${SERVED_BASE_PATH}/${path}`;
24
32
  }
@@ -0,0 +1,20 @@
1
+ // La prueba de pantalla de entrar: sin sesión, la app manda a «Iniciar sesión» y regresa a donde iba al entrar. Entra con
2
+ // el botón de la cuenta de ejemplo (la demostración): la prueba nunca escribe una contraseña.
3
+ //
4
+ // pnpm test
5
+ import {screen} from '@testing-library/react';
6
+ import {describe, expect, it} from 'vitest';
7
+
8
+ import {openScreen} from '@/testing/openScreen.jsx';
9
+
10
+ describe('entrar', () => {
11
+ it('sin sesión pide entrar y después abre la pantalla que se pidió', async () => {
12
+ const {user, router} = await openScreen('/', {signedIn: false});
13
+ expect(await screen.findByRole('heading', {name: 'Iniciar sesión'})).toBeTruthy();
14
+
15
+ await user.click(screen.getByRole('button', {name: 'Entrar con la cuenta de ejemplo'}));
16
+
17
+ expect(await screen.findByRole('heading', {name: 'Bienvenida'})).toBeTruthy();
18
+ expect(router.state.location.pathname).toBe('/');
19
+ });
20
+ });
@@ -0,0 +1,16 @@
1
+ // La prueba de pantalla del inicio: la app completa abre en «/» y enseña la bienvenida con sus primeros pasos. Es el ejemplo
2
+ // para las demás: abrir la pantalla, buscar lo que ve la persona por su papel y su nombre, y picar como ella.
3
+ //
4
+ // pnpm test
5
+ import {screen} from '@testing-library/react';
6
+ import {describe, expect, it} from 'vitest';
7
+
8
+ import {openScreen} from '@/testing/openScreen.jsx';
9
+
10
+ describe('el inicio', () => {
11
+ it('enseña la bienvenida y cómo seguir con el CLI', async () => {
12
+ await openScreen('/');
13
+ expect(await screen.findByRole('heading', {name: 'Bienvenida'})).toBeTruthy();
14
+ expect(screen.getByText('pnpm exec calcita make:module orders')).toBeTruthy();
15
+ });
16
+ });
@@ -0,0 +1,70 @@
1
+ // Lo que jsdom (el navegador de mentira de las pruebas) no trae y la app sí usa.
2
+ //
3
+ // Cada hueco se llena con lo mínimo para que la pantalla se dibuje. Ninguna prueba depende de que esto haga algo de
4
+ // verdad: jsdom no mide ni pinta, así que tamaños, scroll y medidas de pantalla no se prueban aquí (eso es de un navegador
5
+ // de verdad, con Playwright).
6
+ //
7
+ // Funciones puras, no renderizan: function (regla de la casa).
8
+
9
+ /**
10
+ * Los archivos que la pantalla mandó guardar (una descarga), en orden: {fileName, href}. jsdom no sabe descargar; aquí se
11
+ * anota el nombre para que la prueba revise qué se habría guardado.
12
+ */
13
+ export const savedFiles = [];
14
+
15
+ /** Observador de tamaño o de vista que nunca avisa: en jsdom todo mide 0 y no cambia. */
16
+ class SilentObserver {
17
+ observe() {}
18
+
19
+ unobserve() {}
20
+
21
+ disconnect() {}
22
+
23
+ takeRecords() {
24
+ return [];
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Consulta de medios que nunca coincide: la pantalla se dibuja como en escritorio y en modo claro.
30
+ *
31
+ * @param {string} media la consulta, p. ej. '(prefers-color-scheme: dark)'
32
+ * @returns {MediaQueryList}
33
+ */
34
+ function neverMatchingMediaQuery(media) {
35
+ return {
36
+ matches: false,
37
+ media,
38
+ onchange: null,
39
+ addEventListener() {},
40
+ removeEventListener() {},
41
+ addListener() {},
42
+ removeListener() {},
43
+ dispatchEvent() {
44
+ return false;
45
+ },
46
+ };
47
+ }
48
+
49
+ /** Llena los huecos de jsdom. */
50
+ export function fillBrowserGaps() {
51
+ window.ResizeObserver ??= SilentObserver;
52
+ window.IntersectionObserver ??= SilentObserver;
53
+ window.matchMedia ??= neverMatchingMediaQuery;
54
+ // ir arriba al cambiar de pantalla y llevar a la vista lo elegido
55
+ window.scrollTo = () => {};
56
+ window.HTMLElement.prototype.scrollTo ??= function scrollTo() {};
57
+ window.HTMLElement.prototype.scrollIntoView ??= function scrollIntoView() {};
58
+ // descargas y vistas previas de archivos: una dirección de mentira
59
+ window.URL.createObjectURL ??= () => 'blob:prueba';
60
+ window.URL.revokeObjectURL ??= () => {};
61
+ // la liga de descarga se anota en savedFiles en lugar de navegar (jsdom no navega y lo diría con un error)
62
+ const followLink = window.HTMLAnchorElement.prototype.click;
63
+ window.HTMLAnchorElement.prototype.click = function click() {
64
+ if (this.hasAttribute('download')) {
65
+ savedFiles.push({fileName: this.getAttribute('download'), href: this.href});
66
+ return;
67
+ }
68
+ followLink.call(this);
69
+ };
70
+ }
@@ -0,0 +1,58 @@
1
+ // Abre una pantalla como la abre una persona: la app COMPLETA (sus rutas, el marco, la sesión, el menú y los avisos) en una
2
+ // dirección, con el servidor de mentira contestando (src/fakeServer; sin VITE_API_URL).
3
+ //
4
+ // const {user} = await openScreen('/orders');
5
+ // await screen.findByRole('heading', {name: 'Pedidos'});
6
+ // await user.click(screen.getByRole('button', {name: 'Guardar'}));
7
+ //
8
+ // Por qué la app completa y no el componente suelto: la prueba pasa por lo mismo que la persona (permisos, menú, textos y
9
+ // avisos) y no hay un segundo armado de proveedores que se desfase del de la app.
10
+ //
11
+ // OJO: para esperar a que algo desaparezca, waitForElementToBeRemoved; nunca un waitFor que compare con null: en cada intento
12
+ // imprime la página entera y con la app completa se acaba la memoria.
13
+ import {render} from '@testing-library/react';
14
+ import userEvent from '@testing-library/user-event';
15
+ import {RouterProvider, createMemoryRouter} from 'react-router';
16
+ import CssBaseline from '@mui/material/CssBaseline';
17
+ import {ThemeProvider} from '@mui/material/styles';
18
+
19
+ import {appRoutes} from '@/app/appRouter.jsx';
20
+ import {appTheme} from '@/app/appTheme.js';
21
+ //#if auth
22
+ import {DEMO_SESSION_STORAGE_KEY, DEMO_USER} from '@/core/accountConnector.js';
23
+ //#endif
24
+
25
+ //#if auth
26
+ /**
27
+ * Abre la app en una dirección.
28
+ *
29
+ * @param {string} path la dirección de la pantalla, con su query (p. ej. '/orders?status=pending')
30
+ * @param {{signedIn?: boolean}} [options] signedIn: entra con la persona de la demostración sin pasar por la pantalla de
31
+ * entrar (por omisión, sí); con false se ve la app como alguien sin sesión
32
+ * @returns {Promise<{user: object, router: object}>} la persona que da clics y escribe, y el router (para revisar a dónde fue)
33
+ */
34
+ export async function openScreen(path, {signedIn = true} = {}) {
35
+ if (signedIn) {
36
+ // la demostración recuerda la sesión en este navegador con el id de la persona
37
+ window.localStorage.setItem(DEMO_SESSION_STORAGE_KEY, DEMO_USER.id);
38
+ }
39
+ //#endif
40
+ //#if !auth
41
+ /**
42
+ * Abre la app en una dirección.
43
+ *
44
+ * @param {string} path la dirección de la pantalla, con su query (p. ej. '/orders?status=pending')
45
+ * @returns {Promise<{user: object, router: object}>} la persona que da clics y escribe, y el router (para revisar a dónde fue)
46
+ */
47
+ export async function openScreen(path) {
48
+ //#endif
49
+ const router = createMemoryRouter(appRoutes, {initialEntries: [path]});
50
+ const user = userEvent.setup();
51
+ render(
52
+ <ThemeProvider theme={appTheme}>
53
+ <CssBaseline />
54
+ <RouterProvider router={router} />
55
+ </ThemeProvider>,
56
+ );
57
+ return {user, router};
58
+ }
@@ -0,0 +1,25 @@
1
+ // Lo que necesita cada prueba de pantalla antes de empezar (vite.config.js, test.setupFiles).
2
+ //
3
+ // - Llena lo que jsdom (el navegador de mentira de las pruebas) no trae y la app usa (browserGaps.js).
4
+ // - Los mensajes de yup como llaves de texto, igual que main.jsx: así salen en el idioma activo.
5
+ // - Al terminar cada prueba borra lo que dibujó y lo que guardó el navegador: sin esto, la siguiente prueba vería la sesión,
6
+ // el modo oscuro o los filtros recordados de la anterior.
7
+ import {afterEach} from 'vitest';
8
+ import {cleanup, configure} from '@testing-library/react';
9
+ import {buildYupLocale} from '@stackcraftmx/calcita/forms';
10
+
11
+ import {fillBrowserGaps, savedFiles} from '@/testing/browserGaps.js';
12
+
13
+ fillBrowserGaps();
14
+ buildYupLocale();
15
+
16
+ // findBy… espera hasta 5 s: una pantalla completa (rutas lazy, servidor de mentira con su demora) tarda más que el segundo
17
+ // de fábrica
18
+ configure({asyncUtilTimeout: 5000});
19
+
20
+ afterEach(() => {
21
+ cleanup();
22
+ savedFiles.length = 0;
23
+ window.localStorage.clear();
24
+ window.sessionStorage.clear();
25
+ });