shutro-erp 0.3.3 → 0.3.5

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 (412) hide show
  1. package/dist/account_portal/.kosh/bindings.js +1 -1
  2. package/dist/account_portal/.kosh/schema.d.ts +181 -3
  3. package/dist/account_portal/module.d.ts +359 -6
  4. package/dist/accounting/.kosh/bindings.js +1 -1
  5. package/dist/accounting/.kosh/schema.d.ts +181 -3
  6. package/dist/accounting/index.js +214 -2
  7. package/dist/accounting/module.d.ts +217 -5
  8. package/dist/accounting/module.js +23 -2
  9. package/dist/calendar/.kosh/bindings.d.ts +22 -0
  10. package/dist/calendar/.kosh/bindings.js +15 -0
  11. package/dist/calendar/.kosh/schema.d.ts +795 -0
  12. package/dist/calendar/demo.d.ts +6 -0
  13. package/dist/calendar/demo.js +24 -0
  14. package/dist/calendar/index.d.ts +3 -0
  15. package/dist/calendar/index.js +291 -0
  16. package/dist/calendar/module.d.ts +1705 -0
  17. package/dist/calendar/module.js +180 -0
  18. package/dist/cli/init.js +48 -0
  19. package/dist/cli/templates.d.ts +1 -1
  20. package/dist/cli/templates.js +137 -0
  21. package/dist/contacts/.kosh/bindings.js +1 -1
  22. package/dist/contacts/.kosh/schema.d.ts +205 -3
  23. package/dist/contacts/demo.js +30 -1
  24. package/dist/contacts/index.d.ts +1 -1
  25. package/dist/contacts/index.js +155 -9
  26. package/dist/contacts/module.d.ts +338 -2
  27. package/dist/contacts/module.js +82 -5
  28. package/dist/crm/.kosh/bindings.d.ts +22 -0
  29. package/dist/crm/.kosh/bindings.js +15 -0
  30. package/dist/crm/.kosh/schema.d.ts +799 -0
  31. package/dist/crm/demo.d.ts +6 -0
  32. package/dist/crm/demo.js +45 -0
  33. package/dist/crm/index.d.ts +3 -0
  34. package/dist/crm/index.js +361 -0
  35. package/dist/crm/module.d.ts +1843 -0
  36. package/dist/crm/module.js +207 -0
  37. package/dist/event/.kosh/bindings.d.ts +22 -0
  38. package/dist/event/.kosh/bindings.js +15 -0
  39. package/dist/event/.kosh/schema.d.ts +843 -0
  40. package/dist/event/demo.d.ts +6 -0
  41. package/dist/event/demo.js +65 -0
  42. package/dist/event/index.d.ts +14 -0
  43. package/dist/event/index.js +333 -0
  44. package/dist/event/mails.d.ts +15 -0
  45. package/dist/event/mails.js +112 -0
  46. package/dist/event/module.d.ts +2800 -0
  47. package/dist/event/module.js +323 -0
  48. package/dist/event/web/CheckInBox.d.ts +9 -0
  49. package/dist/event/web/CheckInBox.js +87 -0
  50. package/dist/event/web/index.d.ts +11 -0
  51. package/dist/event/web/index.js +16 -0
  52. package/dist/event/web/messages.d.ts +2 -0
  53. package/dist/event/web/messages.js +23 -0
  54. package/dist/event_account/.kosh/bindings.d.ts +22 -0
  55. package/dist/event_account/.kosh/bindings.js +15 -0
  56. package/dist/event_account/.kosh/schema.d.ts +1639 -0
  57. package/dist/event_account/index.d.ts +3 -0
  58. package/dist/event_account/index.js +28 -0
  59. package/dist/event_account/module.d.ts +6968 -0
  60. package/dist/event_account/module.js +90 -0
  61. package/dist/hr/.kosh/bindings.d.ts +22 -0
  62. package/dist/hr/.kosh/bindings.js +15 -0
  63. package/dist/hr/.kosh/schema.d.ts +878 -0
  64. package/dist/hr/demo.d.ts +6 -0
  65. package/dist/hr/demo.js +60 -0
  66. package/dist/hr/index.d.ts +3 -0
  67. package/dist/hr/index.js +483 -0
  68. package/dist/hr/module.d.ts +2611 -0
  69. package/dist/hr/module.js +350 -0
  70. package/dist/integrations/index.js +12 -0
  71. package/dist/integrations/web/OAuthAppsPage.d.ts +9 -0
  72. package/dist/integrations/web/OAuthAppsPage.js +79 -0
  73. package/dist/integrations/web/index.d.ts +1 -0
  74. package/dist/integrations/web/index.js +3 -0
  75. package/dist/integrations/web/messages.js +58 -0
  76. package/dist/l10n_bd/.kosh/bindings.js +1 -1
  77. package/dist/l10n_bd/.kosh/schema.d.ts +181 -3
  78. package/dist/l10n_bd/module.d.ts +359 -6
  79. package/dist/l10n_in/.kosh/bindings.js +1 -1
  80. package/dist/l10n_in/.kosh/schema.d.ts +181 -3
  81. package/dist/l10n_in/module.d.ts +380 -7
  82. package/dist/maintenance/.kosh/bindings.d.ts +22 -0
  83. package/dist/maintenance/.kosh/bindings.js +15 -0
  84. package/dist/maintenance/.kosh/schema.d.ts +911 -0
  85. package/dist/maintenance/demo.d.ts +6 -0
  86. package/dist/maintenance/demo.js +60 -0
  87. package/dist/maintenance/index.d.ts +3 -0
  88. package/dist/maintenance/index.js +422 -0
  89. package/dist/maintenance/module.d.ts +3545 -0
  90. package/dist/maintenance/module.js +446 -0
  91. package/dist/maintenance_stock/.kosh/bindings.d.ts +22 -0
  92. package/dist/maintenance_stock/.kosh/bindings.js +15 -0
  93. package/dist/maintenance_stock/.kosh/schema.d.ts +1371 -0
  94. package/dist/maintenance_stock/index.d.ts +9 -0
  95. package/dist/maintenance_stock/index.js +52 -0
  96. package/dist/maintenance_stock/module.d.ts +6953 -0
  97. package/dist/maintenance_stock/module.js +192 -0
  98. package/dist/messaging/web/index.js +1 -1
  99. package/dist/modules-sdk/index.d.ts +1 -1
  100. package/dist/modules-sdk/manifest.d.ts +70 -0
  101. package/dist/modules-sdk/manifest.js +35 -1
  102. package/dist/mrp/.kosh/bindings.d.ts +22 -0
  103. package/dist/mrp/.kosh/bindings.js +15 -0
  104. package/dist/mrp/.kosh/schema.d.ts +1378 -0
  105. package/dist/mrp/demo.d.ts +7 -0
  106. package/dist/mrp/demo.js +42 -0
  107. package/dist/mrp/index.d.ts +3 -0
  108. package/dist/mrp/index.js +392 -0
  109. package/dist/mrp/module.d.ts +8608 -0
  110. package/dist/mrp/module.js +695 -0
  111. package/dist/mrp_account/.kosh/bindings.d.ts +22 -0
  112. package/dist/mrp_account/.kosh/bindings.js +15 -0
  113. package/dist/mrp_account/.kosh/schema.d.ts +2106 -0
  114. package/dist/mrp_account/index.d.ts +3 -0
  115. package/dist/mrp_account/index.js +22 -0
  116. package/dist/mrp_account/module.d.ts +20840 -0
  117. package/dist/mrp_account/module.js +123 -0
  118. package/dist/notify/web/index.js +1 -1
  119. package/dist/payment/.kosh/bindings.js +1 -1
  120. package/dist/payment/.kosh/schema.d.ts +181 -3
  121. package/dist/payment/module.d.ts +359 -6
  122. package/dist/portal/.kosh/bindings.js +1 -1
  123. package/dist/portal/.kosh/schema.d.ts +205 -3
  124. package/dist/portal/module.d.ts +166 -2
  125. package/dist/pos/.kosh/bindings.d.ts +22 -0
  126. package/dist/pos/.kosh/bindings.js +15 -0
  127. package/dist/pos/.kosh/schema.d.ts +1690 -0
  128. package/dist/pos/accounting.d.ts +10 -0
  129. package/dist/pos/accounting.js +149 -0
  130. package/dist/pos/demo.d.ts +6 -0
  131. package/dist/pos/demo.js +90 -0
  132. package/dist/pos/index.d.ts +9 -0
  133. package/dist/pos/index.js +349 -0
  134. package/dist/pos/module.d.ts +9636 -0
  135. package/dist/pos/module.js +423 -0
  136. package/dist/pos/routes.d.ts +39 -0
  137. package/dist/pos/routes.js +368 -0
  138. package/dist/pos/web/Register.d.ts +9 -0
  139. package/dist/pos/web/Register.js +252 -0
  140. package/dist/pos/web/index.d.ts +11 -0
  141. package/dist/pos/web/index.js +16 -0
  142. package/dist/pos/web/messages.d.ts +2 -0
  143. package/dist/pos/web/messages.js +87 -0
  144. package/dist/pos_stock/.kosh/bindings.d.ts +22 -0
  145. package/dist/pos_stock/.kosh/bindings.js +15 -0
  146. package/dist/pos_stock/.kosh/schema.d.ts +2015 -0
  147. package/dist/pos_stock/index.d.ts +9 -0
  148. package/dist/pos_stock/index.js +35 -0
  149. package/dist/pos_stock/module.d.ts +11935 -0
  150. package/dist/pos_stock/module.js +115 -0
  151. package/dist/product/.kosh/bindings.js +1 -1
  152. package/dist/product/.kosh/schema.d.ts +178 -1
  153. package/dist/product/index.js +81 -4
  154. package/dist/product/module.d.ts +43 -1
  155. package/dist/product/module.js +9 -2
  156. package/dist/project/.kosh/bindings.js +1 -1
  157. package/dist/project/.kosh/schema.d.ts +181 -3
  158. package/dist/project/index.js +147 -7
  159. package/dist/project/module.d.ts +210 -2
  160. package/dist/project/module.js +52 -5
  161. package/dist/project_portal/.kosh/bindings.js +1 -1
  162. package/dist/project_portal/.kosh/schema.d.ts +181 -3
  163. package/dist/project_portal/module.d.ts +353 -4
  164. package/dist/purchase/.kosh/bindings.js +1 -1
  165. package/dist/purchase/.kosh/schema.d.ts +181 -3
  166. package/dist/purchase/index.js +163 -3
  167. package/dist/purchase/module.d.ts +215 -3
  168. package/dist/purchase/module.js +21 -3
  169. package/dist/purchase_account/.kosh/bindings.js +1 -1
  170. package/dist/purchase_account/.kosh/schema.d.ts +182 -3
  171. package/dist/purchase_account/index.js +26 -1
  172. package/dist/purchase_account/module.d.ts +393 -7
  173. package/dist/purchase_portal/.kosh/bindings.js +1 -1
  174. package/dist/purchase_portal/.kosh/schema.d.ts +181 -3
  175. package/dist/purchase_portal/module.d.ts +366 -5
  176. package/dist/purchase_stock/.kosh/bindings.d.ts +22 -0
  177. package/dist/purchase_stock/.kosh/bindings.js +15 -0
  178. package/dist/purchase_stock/.kosh/schema.d.ts +1310 -0
  179. package/dist/purchase_stock/index.d.ts +7 -0
  180. package/dist/purchase_stock/index.js +91 -0
  181. package/dist/purchase_stock/module.d.ts +6763 -0
  182. package/dist/purchase_stock/module.js +144 -0
  183. package/dist/purchase_stock_account/.kosh/bindings.d.ts +22 -0
  184. package/dist/purchase_stock_account/.kosh/bindings.js +15 -0
  185. package/dist/purchase_stock_account/.kosh/schema.d.ts +1959 -0
  186. package/dist/purchase_stock_account/index.d.ts +3 -0
  187. package/dist/purchase_stock_account/index.js +24 -0
  188. package/dist/purchase_stock_account/module.d.ts +35366 -0
  189. package/dist/purchase_stock_account/module.js +162 -0
  190. package/dist/recruitment/.kosh/bindings.d.ts +22 -0
  191. package/dist/recruitment/.kosh/bindings.js +15 -0
  192. package/dist/recruitment/.kosh/schema.d.ts +990 -0
  193. package/dist/recruitment/demo.d.ts +6 -0
  194. package/dist/recruitment/demo.js +45 -0
  195. package/dist/recruitment/index.d.ts +3 -0
  196. package/dist/recruitment/index.js +385 -0
  197. package/dist/recruitment/module.d.ts +3247 -0
  198. package/dist/recruitment/module.js +193 -0
  199. package/dist/recruitment/web/HireButton.d.ts +9 -0
  200. package/dist/recruitment/web/HireButton.js +32 -0
  201. package/dist/recruitment/web/index.d.ts +11 -0
  202. package/dist/recruitment/web/index.js +16 -0
  203. package/dist/recruitment/web/messages.d.ts +2 -0
  204. package/dist/recruitment/web/messages.js +19 -0
  205. package/dist/reporting/documents.d.ts +5 -0
  206. package/dist/reporting/documents.js +28 -1
  207. package/dist/reporting/index.d.ts +1 -1
  208. package/dist/reporting/index.js +4 -2
  209. package/dist/reporting/models.js +9 -0
  210. package/dist/reporting/overviews.d.ts +16 -0
  211. package/dist/reporting/overviews.js +56 -0
  212. package/dist/reporting/routes-documents.js +1 -1
  213. package/dist/reporting/routes.js +138 -80
  214. package/dist/reporting/web/DashboardView.d.ts +11 -0
  215. package/dist/reporting/web/DashboardView.js +2 -2
  216. package/dist/reporting/web/DesignerView.js +5 -0
  217. package/dist/reporting/web/Overview.d.ts +9 -0
  218. package/dist/reporting/web/Overview.js +53 -0
  219. package/dist/reporting/web/index.d.ts +1 -0
  220. package/dist/reporting/web/index.js +4 -1
  221. package/dist/reporting/web/messages.js +9 -0
  222. package/dist/sale_account/.kosh/bindings.js +1 -1
  223. package/dist/sale_account/.kosh/schema.d.ts +186 -3
  224. package/dist/sale_account/index.js +22 -1
  225. package/dist/sale_account/module.d.ts +461 -7
  226. package/dist/sale_portal/.kosh/bindings.js +1 -1
  227. package/dist/sale_portal/.kosh/schema.d.ts +185 -3
  228. package/dist/sale_portal/module.d.ts +434 -5
  229. package/dist/sale_project/.kosh/bindings.js +1 -1
  230. package/dist/sale_project/.kosh/schema.d.ts +187 -3
  231. package/dist/sale_project/module.d.ts +476 -6
  232. package/dist/sale_stock/.kosh/bindings.d.ts +22 -0
  233. package/dist/sale_stock/.kosh/bindings.js +15 -0
  234. package/dist/sale_stock/.kosh/schema.d.ts +1228 -0
  235. package/dist/sale_stock/index.d.ts +7 -0
  236. package/dist/sale_stock/index.js +84 -0
  237. package/dist/sale_stock/module.d.ts +6791 -0
  238. package/dist/sale_stock/module.js +146 -0
  239. package/dist/sales/.kosh/bindings.js +1 -1
  240. package/dist/sales/.kosh/schema.d.ts +185 -3
  241. package/dist/sales/index.js +191 -4
  242. package/dist/sales/module.d.ts +355 -3
  243. package/dist/sales/module.js +78 -3
  244. package/dist/server/api/metadata.d.ts +5 -0
  245. package/dist/server/api/metadata.js +5 -0
  246. package/dist/server/api/rest.js +64 -1
  247. package/dist/server/api/views.js +1 -0
  248. package/dist/server/app-settings.d.ts +7 -0
  249. package/dist/server/app-settings.js +10 -0
  250. package/dist/server/auth/base.js +85 -0
  251. package/dist/server/auth/oauth.d.ts +21 -0
  252. package/dist/server/auth/oauth.js +579 -0
  253. package/dist/server/auth/service.d.ts +4 -0
  254. package/dist/server/auth/service.js +17 -0
  255. package/dist/server/identity.d.ts +2 -0
  256. package/dist/server/images.d.ts +12 -0
  257. package/dist/server/images.js +139 -0
  258. package/dist/server/index.d.ts +2 -0
  259. package/dist/server/index.js +2 -0
  260. package/dist/server/server.d.ts +4 -0
  261. package/dist/server/server.js +9 -1
  262. package/dist/settings/app-settings.d.ts +2 -0
  263. package/dist/settings/app-settings.js +518 -0
  264. package/dist/settings/company.js +3 -1
  265. package/dist/settings/index.d.ts +1 -0
  266. package/dist/settings/index.js +15 -0
  267. package/dist/settings/people.js +32 -6
  268. package/dist/settings/web/AppSettingsPage.d.ts +11 -0
  269. package/dist/settings/web/AppSettingsPage.js +210 -0
  270. package/dist/settings/web/CompaniesPage.js +2 -2
  271. package/dist/settings/web/CompanyPage.js +13 -2
  272. package/dist/settings/web/ConnectedAppsPage.d.ts +7 -0
  273. package/dist/settings/web/ConnectedAppsPage.js +33 -0
  274. package/dist/settings/web/OverviewPage.js +28 -4
  275. package/dist/settings/web/PeoplePage.js +23 -18
  276. package/dist/settings/web/ProfilePage.js +9 -7
  277. package/dist/settings/web/index.d.ts +2 -0
  278. package/dist/settings/web/index.js +8 -0
  279. package/dist/settings/web/messages.js +146 -2
  280. package/dist/stock/.kosh/bindings.d.ts +22 -0
  281. package/dist/stock/.kosh/bindings.js +15 -0
  282. package/dist/stock/.kosh/schema.d.ts +1147 -0
  283. package/dist/stock/demo.d.ts +7 -0
  284. package/dist/stock/demo.js +55 -0
  285. package/dist/stock/index.d.ts +3 -0
  286. package/dist/stock/index.js +674 -0
  287. package/dist/stock/module.d.ts +6330 -0
  288. package/dist/stock/module.js +1325 -0
  289. package/dist/stock/web/ScanBox.d.ts +8 -0
  290. package/dist/stock/web/ScanBox.js +90 -0
  291. package/dist/stock/web/index.d.ts +11 -0
  292. package/dist/stock/web/index.js +16 -0
  293. package/dist/stock/web/messages.d.ts +2 -0
  294. package/dist/stock/web/messages.js +25 -0
  295. package/dist/stock_account/.kosh/bindings.d.ts +22 -0
  296. package/dist/stock_account/.kosh/bindings.js +15 -0
  297. package/dist/stock_account/.kosh/schema.d.ts +1875 -0
  298. package/dist/stock_account/index.d.ts +3 -0
  299. package/dist/stock_account/index.js +130 -0
  300. package/dist/stock_account/module.d.ts +10655 -0
  301. package/dist/stock_account/module.js +339 -0
  302. package/dist/storage/attachments.d.ts +5 -0
  303. package/dist/storage/attachments.js +12 -1
  304. package/dist/storage/models.js +3 -0
  305. package/dist/storage/routes.js +13 -2
  306. package/dist/todo/.kosh/bindings.d.ts +22 -0
  307. package/dist/todo/.kosh/bindings.js +15 -0
  308. package/dist/todo/.kosh/schema.d.ts +732 -0
  309. package/dist/todo/demo.d.ts +6 -0
  310. package/dist/todo/demo.js +35 -0
  311. package/dist/todo/index.d.ts +3 -0
  312. package/dist/todo/index.js +187 -0
  313. package/dist/todo/module.d.ts +1180 -0
  314. package/dist/todo/module.js +130 -0
  315. package/dist/trade_finance/.kosh/bindings.d.ts +22 -0
  316. package/dist/trade_finance/.kosh/bindings.js +15 -0
  317. package/dist/trade_finance/.kosh/schema.d.ts +884 -0
  318. package/dist/trade_finance/demo.d.ts +6 -0
  319. package/dist/trade_finance/demo.js +63 -0
  320. package/dist/trade_finance/index.d.ts +3 -0
  321. package/dist/trade_finance/index.js +413 -0
  322. package/dist/trade_finance/module.d.ts +3779 -0
  323. package/dist/trade_finance/module.js +505 -0
  324. package/dist/trade_finance_account/.kosh/bindings.d.ts +22 -0
  325. package/dist/trade_finance_account/.kosh/bindings.js +15 -0
  326. package/dist/trade_finance_account/.kosh/schema.d.ts +1711 -0
  327. package/dist/trade_finance_account/index.d.ts +3 -0
  328. package/dist/trade_finance_account/index.js +92 -0
  329. package/dist/trade_finance_account/module.d.ts +7928 -0
  330. package/dist/trade_finance_account/module.js +244 -0
  331. package/dist/trade_finance_purchase/.kosh/bindings.d.ts +22 -0
  332. package/dist/trade_finance_purchase/.kosh/bindings.js +15 -0
  333. package/dist/trade_finance_purchase/.kosh/schema.d.ts +1153 -0
  334. package/dist/trade_finance_purchase/index.d.ts +8 -0
  335. package/dist/trade_finance_purchase/index.js +50 -0
  336. package/dist/trade_finance_purchase/module.d.ts +4252 -0
  337. package/dist/trade_finance_purchase/module.js +44 -0
  338. package/dist/trade_finance_sale/.kosh/bindings.d.ts +22 -0
  339. package/dist/trade_finance_sale/.kosh/bindings.js +15 -0
  340. package/dist/trade_finance_sale/.kosh/schema.d.ts +1071 -0
  341. package/dist/trade_finance_sale/index.d.ts +8 -0
  342. package/dist/trade_finance_sale/index.js +50 -0
  343. package/dist/trade_finance_sale/module.d.ts +4280 -0
  344. package/dist/trade_finance_sale/module.js +44 -0
  345. package/dist/ui/api.d.ts +6 -1
  346. package/dist/ui/api.js +2 -1
  347. package/dist/ui/app.js +51 -17
  348. package/dist/ui/components/AppIcon.js +33 -1
  349. package/dist/ui/components/Avatar.d.ts +21 -0
  350. package/dist/ui/components/Avatar.js +71 -0
  351. package/dist/ui/components/primitives.d.ts +2 -0
  352. package/dist/ui/components/primitives.js +7 -1
  353. package/dist/ui/i18n/messages.d.ts +53 -0
  354. package/dist/ui/i18n/messages.js +98 -0
  355. package/dist/ui/index.d.ts +2 -0
  356. package/dist/ui/index.js +2 -0
  357. package/dist/ui/offline/queue.d.ts +5 -1
  358. package/dist/ui/offline/queue.js +2 -0
  359. package/dist/ui/registry.d.ts +12 -1
  360. package/dist/ui/registry.js +10 -1
  361. package/dist/ui/search/ViewTabs.d.ts +14 -0
  362. package/dist/ui/search/ViewTabs.js +42 -0
  363. package/dist/ui/shell/Consent.d.ts +5 -0
  364. package/dist/ui/shell/Consent.js +35 -0
  365. package/dist/ui/shell/Home.js +136 -16
  366. package/dist/ui/shell/Sidebar.js +15 -22
  367. package/dist/ui/views/FormView.js +98 -17
  368. package/dist/ui/views/KanbanView.d.ts +2 -0
  369. package/dist/ui/views/KanbanView.js +32 -3
  370. package/dist/ui/views/ListView.js +155 -31
  371. package/dist/ui/views/StatsRow.d.ts +14 -0
  372. package/dist/ui/views/StatsRow.js +88 -0
  373. package/dist/ui/views/widgets.d.ts +2 -1
  374. package/dist/ui/views/widgets.js +194 -1
  375. package/dist/views/builders.d.ts +19 -2
  376. package/dist/views/builders.js +21 -1
  377. package/dist/views/condition.d.ts +6 -0
  378. package/dist/views/condition.js +20 -0
  379. package/dist/views/documents.d.ts +8 -0
  380. package/dist/views/extend.d.ts +2 -2
  381. package/dist/views/extend.js +11 -1
  382. package/dist/views/index.d.ts +2 -2
  383. package/dist/views/index.js +2 -2
  384. package/dist/views/types.d.ts +52 -5
  385. package/dist/views/validate.js +39 -1
  386. package/dist/website/.kosh/bindings.d.ts +22 -0
  387. package/dist/website/.kosh/bindings.js +15 -0
  388. package/dist/website/.kosh/schema.d.ts +881 -0
  389. package/dist/website/demo.d.ts +6 -0
  390. package/dist/website/demo.js +115 -0
  391. package/dist/website/index.d.ts +9 -0
  392. package/dist/website/index.js +250 -0
  393. package/dist/website/module.d.ts +2987 -0
  394. package/dist/website/module.js +281 -0
  395. package/dist/website/render.d.ts +45 -0
  396. package/dist/website/render.js +102 -0
  397. package/dist/website/routes.d.ts +12 -0
  398. package/dist/website/routes.js +246 -0
  399. package/dist/website/web/PagePreview.d.ts +7 -0
  400. package/dist/website/web/PagePreview.js +33 -0
  401. package/dist/website/web/index.d.ts +11 -0
  402. package/dist/website/web/index.js +16 -0
  403. package/dist/website/web/messages.d.ts +2 -0
  404. package/dist/website/web/messages.js +15 -0
  405. package/dist/website_crm/.kosh/bindings.d.ts +22 -0
  406. package/dist/website_crm/.kosh/bindings.js +15 -0
  407. package/dist/website_crm/.kosh/schema.d.ts +966 -0
  408. package/dist/website_crm/index.d.ts +3 -0
  409. package/dist/website_crm/index.js +39 -0
  410. package/dist/website_crm/module.d.ts +2955 -0
  411. package/dist/website_crm/module.js +44 -0
  412. package/package.json +118 -1
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The data layer of the Trade Finance ↔ Sales bridge (RFC 0021, Trade Finance step 3): an export LC received for a sales order takes its customer, currency and amount; the order lists its LCs.
3
+ */
4
+ import { salesShutro } from '../sales/index.js';
5
+ import { orm } from '../server/orm.js';
6
+ import { tradeFinanceShutro } from '../trade_finance/index.js';
7
+ import { defineModule, extendModel, f, onchange } from './.kosh/bindings.js';
8
+ export const LcSale = extendModel('trade.lc', {
9
+ fields: {
10
+ saleOrder: f.many2one('sale.order', { ondelete: 'set null', label: 'Sales order' }),
11
+ },
12
+ onchange: {
13
+ // The order's counterparty, currency and total; the LC's direction and source document.
14
+ saleOrder: onchange('trade.lc', async (d) => {
15
+ const order = await d.record('saleOrder');
16
+ if (order === null || order === undefined)
17
+ return;
18
+ d.set('direction', 'export');
19
+ d.set('partner', order.partner.id);
20
+ d.set('currency', order.currency.id);
21
+ d.set('amount', order.amountTotal?.amount.toString() ?? null);
22
+ d.set('origin', order.name);
23
+ }),
24
+ },
25
+ });
26
+ export const SaleLcs = extendModel('sale.order', {
27
+ fields: {
28
+ lcs: f.one2many('trade.lc', 'saleOrder', { label: 'Letters of credit' }),
29
+ },
30
+ });
31
+ export const tradeFinanceSaleShutro = defineModule({
32
+ name: 'trade_finance_sale',
33
+ version: '0.1.0',
34
+ depends: [tradeFinanceShutro, salesShutro],
35
+ groups: [
36
+ orm.defineGroup({
37
+ code: 'trade_finance_sale.user',
38
+ label: 'Export LCs for sales orders',
39
+ implies: ['trade_finance.user', 'sales.user'],
40
+ }),
41
+ ],
42
+ models: [],
43
+ extensions: [LcSale, SaleLcs],
44
+ });
package/dist/ui/api.d.ts CHANGED
@@ -36,6 +36,7 @@ export interface ViewField {
36
36
  readonly label: string;
37
37
  readonly required: boolean;
38
38
  readonly readonly: boolean;
39
+ readonly help?: string;
39
40
  readonly target?: string;
40
41
  readonly options?: readonly {
41
42
  readonly value: string;
@@ -74,6 +75,10 @@ export interface CurrentUser {
74
75
  readonly administratorNeedsSecondFactor: boolean;
75
76
  /** The policy requires two-factor sign-in of everyone and it is not on (RFC 0019 §3.5); older servers send none. */
76
77
  readonly twoFactorSetupRequired?: boolean;
78
+ /** The user's photo version, or null (older servers send none). */
79
+ readonly avatar?: string | null;
80
+ /** Each company's logo version, when it has one. */
81
+ readonly logos?: Readonly<Record<string, string>>;
77
82
  }
78
83
  export interface AppEntry {
79
84
  readonly code: string;
@@ -186,7 +191,7 @@ export interface KoshApi {
186
191
  readonly filename: string;
187
192
  }>;
188
193
  /** Sends a file as the raw body (`Content-Type` from the file, `X-Filename` URI-encoded); the JSON answer. */
189
- upload<T>(path: string, file: Blob, filename: string): Promise<T>;
194
+ upload<T>(path: string, file: Blob, filename: string, extra?: Readonly<Record<string, string>>): Promise<T>;
190
195
  /**
191
196
  * POSTs `body` and reads the server-sent events of the answer (`event:` and JSON `data:` lines), calling
192
197
  * `onEvent` for each (`message` when the event has no name) until the stream ends. @throws {ApiError} when the
package/dist/ui/api.js CHANGED
@@ -226,8 +226,9 @@ export function createKoshApi(options = {}) {
226
226
  if (buffered.trim() !== '')
227
227
  flush(buffered);
228
228
  },
229
- async upload(path, file, filename) {
229
+ async upload(path, file, filename, extra = {}) {
230
230
  const headers = {
231
+ ...extra,
231
232
  'content-type': file.type === '' ? 'application/octet-stream' : file.type,
232
233
  'x-filename': encodeURIComponent(filename),
233
234
  };
package/dist/ui/app.js CHANGED
@@ -12,11 +12,12 @@ import { compileSearch, decodeSearch, encodeSearch } from '../search/index.js';
12
12
  import { onlineManager, QueryClient, QueryClientProvider, useQuery, useQueryClient, } from '@tanstack/react-query';
13
13
  import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client';
14
14
  import { createRootRoute, createRoute, createRouter, Link, Navigate, Outlet, RouterProvider, useNavigate, useParams, useSearch, } from '@tanstack/react-router';
15
- import { CalendarDays, ChartColumn, ChartGantt, ChevronRight, ChevronsUpDown, Columns3, Grid2x2, Languages, LayoutDashboard, List, LogOut, Map as MapIcon, Monitor, Moon, Network, PanelLeft, Plus, Search, Sheet, Sun, Table2, } from 'lucide-react';
15
+ import { CalendarDays, ChartColumn, ChartGantt, ChevronDown, ChevronRight, ChevronsUpDown, Columns3, Grid2x2, House, Languages, LayoutDashboard, List, LogOut, Map as MapIcon, Monitor, Moon, Network, PanelLeft, Plus, Search, Sheet, Sun, Table2, } from 'lucide-react';
16
16
  import { Direction } from 'radix-ui';
17
17
  import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from 'react';
18
18
  import { I18nextProvider, useTranslation } from 'react-i18next';
19
19
  import { ApiError } from './api.js';
20
+ import { Avatar, imageUrl } from './components/Avatar.js';
20
21
  import { Alert, Button, Menu, MenuCheckboxItem, MenuContent, MenuItem, MenuLabel, MenuSeparator, MenuTrigger, Spinner, } from './components/primitives.js';
21
22
  import { KoshProvider, useKosh } from './context.js';
22
23
  import { DesignProvider } from './design.js';
@@ -29,8 +30,10 @@ import { setPrefill, takePrefill } from './prefill.js';
29
30
  import { onAppUpdate, registerServiceWorker } from './pwa/register.js';
30
31
  import { createRealtimeClient } from './realtime.js';
31
32
  import { SearchBar } from './search/SearchBar.js';
33
+ import { ViewTabs } from './search/ViewTabs.js';
32
34
  import { Activate } from './shell/Activate.js';
33
35
  import { CommandPalette, PaletteContext } from './shell/CommandPalette.js';
36
+ import { Consent } from './shell/Consent.js';
34
37
  import { Home } from './shell/Home.js';
35
38
  import { Login } from './shell/Login.js';
36
39
  import { OfflineMenu } from './shell/OfflineMenu.js';
@@ -142,7 +145,8 @@ function CompanyBlock({ me, company, collapsed, onChange, }) {
142
145
  enabled: me.companies.length > 0,
143
146
  });
144
147
  const name = names.data?.find((c) => c.id === company)?.displayName ?? t('app.name');
145
- const badge = (_jsx("span", { "aria-hidden": true, className: "grid size-7 shrink-0 place-items-center rounded-md bg-primary text-xs font-semibold text-primary-foreground", children: name.slice(0, 1).toUpperCase() }));
148
+ // The company's logo, else its initial.
149
+ const badge = (_jsx(Avatar, { name: name.slice(0, 1), src: imageUrl('company', company, me.logos?.[String(company)]), shape: "square", className: "font-semibold" }));
146
150
  if (collapsed)
147
151
  return _jsx("span", { title: name, children: badge });
148
152
  if (me.companies.length < 2)
@@ -153,19 +157,16 @@ function CompanyBlock({ me, company, collapsed, onChange, }) {
153
157
  onChange(c.id);
154
158
  }, children: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx("span", { "aria-hidden": true, className: "grid size-5 place-items-center rounded bg-muted text-[10px] font-semibold", children: c.displayName.slice(0, 1).toUpperCase() }), c.displayName] }) }, c.id)))] })] }));
155
159
  }
156
- /** The user at the bottom of the sidebar: their pages and signing out. */
157
- function UserMenu({ user, collapsed, }) {
160
+ function UserMenu({ user, collapsed, corner, }) {
158
161
  const { t } = useTranslation();
159
- const { api, registry, persister } = useServices();
162
+ const { api, registry, persister, lang, setLang } = useServices();
163
+ const { theme, setTheme, density, setDensity } = useKosh();
160
164
  const queryClient = useQueryClient();
161
165
  const navigate = useNavigate();
162
- const initials = user.name
163
- .split(/\s+/)
164
- .map((w) => w.slice(0, 1))
165
- .join('')
166
- .slice(0, 2)
167
- .toUpperCase();
168
- return (_jsxs(Menu, { children: [_jsx(MenuTrigger, { asChild: true, children: _jsxs("button", { type: "button", "aria-label": user.name, className: cn('flex w-full min-w-0 items-center gap-2 rounded-md px-1.5 py-1.5 text-sm hover:bg-muted', collapsed && 'justify-center'), children: [_jsx("span", { "aria-hidden": true, className: "grid size-7 shrink-0 place-items-center rounded-full bg-muted text-xs font-medium", children: initials }), collapsed ? null : _jsx("span", { className: "truncate", children: user.name })] }) }), _jsxs(MenuContent, { align: "start", side: "top", children: [_jsx(MenuLabel, { children: user.name }), registry.userPages().map((p) => (_jsx(MenuItem, { onSelect: () => void navigate({ to: '/me/$page', params: { page: p.name } }), children: t(p.label) }, p.name))), _jsxs(MenuItem, { onSelect: async () => {
166
+ return (_jsxs(Menu, { children: [_jsx(MenuTrigger, { asChild: true, children: _jsxs("button", { type: "button", "aria-label": user.name, "data-kosh-account": corner === undefined ? 'sidebar' : 'corner', className: cn('flex min-w-0 items-center gap-2 rounded-md px-1.5 py-1.5 text-sm hover:bg-muted', corner === undefined ? 'w-full' : 'rounded-full border bg-surface pe-3', collapsed && 'justify-center'), children: [_jsx(Avatar, { name: user.name, src: imageUrl('user', user.id, user.avatar) }), collapsed ? null : _jsx("span", { className: "truncate", children: user.name }), corner === undefined ? null : (_jsx(ChevronDown, { className: "size-3.5 text-muted-foreground", "aria-hidden": true }))] }) }), _jsxs(MenuContent, { align: corner === undefined ? 'start' : 'end', side: corner === undefined ? 'top' : 'bottom', className: corner === undefined ? undefined : 'max-h-[80vh] w-64 overflow-y-auto', children: [_jsxs(MenuLabel, { children: [user.name, _jsx("span", { className: "block text-xs font-normal text-muted-foreground", children: user.login })] }), registry.userPages().map((p) => (_jsx(MenuItem, { onSelect: () => void navigate({ to: '/me/$page', params: { page: p.name } }), children: t(p.label) }, p.name))), corner === undefined ? null : (_jsxs(_Fragment, { children: [corner.companies.length < 2 ? null : (_jsxs(_Fragment, { children: [_jsx(MenuSeparator, {}), _jsx(MenuLabel, { children: t('shell.company') }), corner.companies.map((c) => (_jsx(MenuCheckboxItem, { checked: c.id === corner.current, onCheckedChange: () => {
167
+ if (c.id !== corner.current)
168
+ corner.onSwitch(c.id);
169
+ }, children: c.name }, c.id)))] })), _jsx(MenuSeparator, {}), _jsx(MenuLabel, { children: t('shell.language') }), LANGUAGES.map((l) => (_jsx(MenuCheckboxItem, { checked: lang === l.code, onCheckedChange: () => setLang(l.code), children: _jsx("span", { lang: l.code.replace('_', '-'), children: l.name }) }, l.code))), _jsx(MenuSeparator, {}), _jsx(MenuLabel, { children: t('shell.theme') }), ['light', 'dark', 'system'].map((choice) => (_jsx(MenuCheckboxItem, { checked: theme === choice, onCheckedChange: () => setTheme(choice), children: t(`shell.${choice}`) }, choice))), _jsx(MenuLabel, { children: t('shell.density') }), ['comfortable', 'compact'].map((choice) => (_jsx(MenuCheckboxItem, { checked: density === choice, onCheckedChange: () => setDensity(choice), children: t(`views.${choice}`) }, choice)))] })), _jsx(MenuSeparator, {}), _jsxs(MenuItem, { onSelect: async () => {
169
170
  await api.logout();
170
171
  queryClient.clear();
171
172
  await dropOfflineCache(persister);
@@ -247,11 +248,21 @@ function Shell() {
247
248
  setDrawer(false);
248
249
  setPalette(true);
249
250
  }, top: _jsx(CompanyBlock, { me: user, company: current, collapsed: inDrawer ? false : collapsed, onChange: switchCompany }), bottom: _jsx(UserMenu, { user: user, collapsed: inDrawer ? false : collapsed }), onNavigate: inDrawer ? () => setDrawer(false) : undefined }));
250
- return (_jsx(KoshProvider, { api: api, registry: registry, uid: user.id, companyId: current, lang: lang, tz: Intl.DateTimeFormat().resolvedOptions().timeZone, grouping: user.groupings?.[String(current)] ?? 'international', accent: user.accents?.[String(current)] ?? null, ...(realtime === undefined ? {} : { realtime }), ...(offline === undefined ? {} : { offline }), switchCompany: switchCompany, children: _jsx(ToastProvider, { children: _jsxs(DesignProvider, { children: [_jsx(OfflineAgent, { uid: user.id }), _jsxs(PaletteContext.Provider, { value: openPalette, children: [_jsx("a", { href: "#content", className: "sr-only focus:not-sr-only focus:absolute focus:start-2 focus:top-2 focus:z-50 focus:rounded focus:bg-surface focus:px-3 focus:py-2", children: t('shell.skip') }), _jsxs("div", { className: "flex min-h-screen", children: [_jsx("aside", { "aria-label": t('shell.sidebar'), className: cn('sticky top-0 hidden h-screen shrink-0 border-e bg-surface transition-[width] lg:block', collapsed ? 'w-14' : 'w-60'), children: sidebar(false) }), _jsx(SidebarDrawer, { open: drawer, onOpenChange: setDrawer, children: sidebar(true) }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx("header", { className: "sticky top-0 z-30 border-b bg-surface", children: _jsxs("div", { className: "flex h-12 items-center gap-2 px-3 lg:px-4", children: [_jsx(Button, { variant: "ghost", size: "icon", className: "lg:hidden", "aria-label": t('shell.openMenu'), onClick: () => setDrawer(true), children: _jsx(PanelLeft, { className: "rtl:rotate-180", "aria-hidden": true }) }), _jsx("nav", { "aria-label": t('shell.location'), className: "flex min-w-0 flex-1 items-center gap-1.5 text-sm", children: app === undefined ? (_jsx("span", { className: "font-semibold", children: userPage !== undefined
251
+ return (_jsx(KoshProvider, { api: api, registry: registry, uid: user.id, companyId: current, lang: lang, tz: Intl.DateTimeFormat().resolvedOptions().timeZone, grouping: user.groupings?.[String(current)] ?? 'international', accent: user.accents?.[String(current)] ?? null, ...(realtime === undefined ? {} : { realtime }), ...(offline === undefined ? {} : { offline }), switchCompany: switchCompany, children: _jsx(ToastProvider, { children: _jsxs(DesignProvider, { children: [_jsx(OfflineAgent, { uid: user.id }), _jsxs(PaletteContext.Provider, { value: openPalette, children: [_jsx("a", { href: "#content", className: "sr-only focus:not-sr-only focus:absolute focus:start-2 focus:top-2 focus:z-50 focus:rounded focus:bg-surface focus:px-3 focus:py-2", children: t('shell.skip') }), params.app === undefined ? (_jsxs("div", { className: "flex min-h-screen flex-col", children: [_jsxs("div", { className: "flex h-14 items-center gap-2 px-4 lg:px-6", children: [userPage === undefined ? null : (_jsxs(Link, { to: "/", className: "flex items-center gap-1.5 rounded-md px-2 py-1 text-sm text-muted-foreground hover:bg-muted hover:text-foreground", children: [_jsx(House, { className: "size-4", "aria-hidden": true }), t('shell.home')] })), _jsx("span", { className: "flex-1" }), registry
252
+ .headerItems()
253
+ .filter((item) => item.home)
254
+ .map(({ name, component: Item }) => (_jsx(Item, { api: api }, name))), _jsx(UserMenu, { user: user, collapsed: false, corner: {
255
+ companies: (companyNames.data ?? []).map((c) => ({
256
+ id: c.id,
257
+ name: c.displayName,
258
+ })),
259
+ current,
260
+ onSwitch: switchCompany,
261
+ } })] }), user.administratorNeedsSecondFactor ? (_jsx("div", { className: "px-4 pt-3", children: _jsx(Alert, { tone: "info", children: t('auth.adminNeeds2fa') }) })) : null, _jsx("main", { id: "content", className: "mx-auto w-full max-w-[100rem] flex-1 p-4 lg:p-6", children: _jsx(Outlet, {}) })] })) : (_jsxs("div", { className: "flex min-h-screen", children: [_jsx("aside", { "aria-label": t('shell.sidebar'), className: cn('sticky top-0 hidden h-screen shrink-0 border-e bg-surface transition-[width] lg:block', collapsed ? 'w-14' : 'w-60'), children: sidebar(false) }), _jsx(SidebarDrawer, { open: drawer, onOpenChange: setDrawer, children: sidebar(true) }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx("header", { className: "sticky top-0 z-30 border-b bg-surface", children: _jsxs("div", { className: "flex h-12 items-center gap-2 px-3 lg:px-4", children: [_jsx(Button, { variant: "ghost", size: "icon", className: "lg:hidden", "aria-label": t('shell.openMenu'), onClick: () => setDrawer(true), children: _jsx(PanelLeft, { className: "rtl:rotate-180", "aria-hidden": true }) }), _jsx("nav", { "aria-label": t('shell.location'), className: "flex min-w-0 flex-1 items-center gap-1.5 text-sm", children: app === undefined ? (_jsx("span", { className: "font-semibold", children: userPage !== undefined
251
262
  ? t(userPage.label)
252
263
  : params.app === undefined
253
264
  ? t('shell.home')
254
- : t('app.name') })) : (_jsxs(_Fragment, { children: [_jsx(Link, { to: "/$app", params: { app: app.code }, className: "font-semibold hover:underline", children: app.label }), section === undefined ? null : (_jsxs(_Fragment, { children: [_jsx(ChevronRight, { className: "size-3.5 text-muted-foreground rtl:rotate-180", "aria-hidden": true }), _jsx(Link, { to: "/$app/a/$action", params: { app: app.code, action: params.action ?? '' }, className: "truncate text-muted-foreground hover:text-foreground hover:underline", children: section })] }))] })) }), _jsx(Button, { variant: "ghost", size: "icon", className: "lg:hidden", "aria-label": t('palette.button'), onClick: () => setPalette(true), children: _jsx(Search, { "aria-hidden": true }) }), _jsx(OfflineMenu, {}), registry.headerItems().map(({ name, component: Item }) => (_jsx(Item, { api: api }, name))), _jsx(LanguageMenu, {}), _jsx(ThemeMenu, {})] }) }), user.administratorNeedsSecondFactor ? (_jsx("div", { className: "px-4 pt-3", children: _jsx(Alert, { tone: "info", children: t('auth.adminNeeds2fa') }) })) : null, _jsx("main", { id: "content", className: "mx-auto w-full max-w-[100rem] flex-1 p-4 lg:p-6", children: _jsx(Outlet, {}) })] })] }), _jsx(CommandPalette, { open: palette, onOpenChange: setPalette, api: api, onShortcuts: openHelp, companies: (companyNames.data ?? [])
265
+ : t('app.name') })) : (_jsxs(_Fragment, { children: [_jsx(Link, { to: "/$app", params: { app: app.code }, className: "font-semibold hover:underline", children: app.label }), section === undefined ? null : (_jsxs(_Fragment, { children: [_jsx(ChevronRight, { className: "size-3.5 text-muted-foreground rtl:rotate-180", "aria-hidden": true }), _jsx(Link, { to: "/$app/a/$action", params: { app: app.code, action: params.action ?? '' }, className: "truncate text-muted-foreground hover:text-foreground hover:underline", children: section })] }))] })) }), _jsx(Button, { variant: "ghost", size: "icon", className: "lg:hidden", "aria-label": t('palette.button'), onClick: () => setPalette(true), children: _jsx(Search, { "aria-hidden": true }) }), _jsx(OfflineMenu, {}), registry.headerItems().map(({ name, component: Item }) => (_jsx(Item, { api: api }, name))), _jsx(LanguageMenu, {}), _jsx(ThemeMenu, {})] }) }), user.administratorNeedsSecondFactor ? (_jsx("div", { className: "px-4 pt-3", children: _jsx(Alert, { tone: "info", children: t('auth.adminNeeds2fa') }) })) : null, _jsx("main", { id: "content", className: "mx-auto w-full max-w-[100rem] flex-1 p-4 lg:p-6", children: _jsx(Outlet, {}) })] })] })), _jsx(CommandPalette, { open: palette, onOpenChange: setPalette, api: api, onShortcuts: openHelp, companies: (companyNames.data ?? [])
255
266
  .filter((c) => c.id !== current)
256
267
  .map((c) => ({ id: c.id, name: c.displayName })), onSwitchCompany: switchCompany }), _jsx(ShortcutsHelp, { open: help, onOpenChange: setHelp })] })] }) }) }));
257
268
  }
@@ -291,10 +302,11 @@ function useServedView(action, type) {
291
302
  function ClientPage({ action }) {
292
303
  const { registry, api } = useServices();
293
304
  const { t } = useTranslation();
305
+ const params = useParams({ strict: false });
294
306
  const Page = registry.page(action.page);
295
307
  if (Page === undefined)
296
308
  return _jsx(Alert, { children: t('app.unknownPage', { page: action.page }) });
297
- return _jsx(Page, { api: api, ...(action.label === undefined ? {} : { label: action.label }) });
309
+ return (_jsx(Page, { api: api, ...(action.label === undefined ? {} : { label: action.label }), ...(params.app === undefined ? {} : { app: params.app }), ...(params.action === undefined ? {} : { action: params.action }) }));
298
310
  }
299
311
  function ActionPage() {
300
312
  const { action: actionId } = useParams({ strict: false });
@@ -390,7 +402,7 @@ function WindowActionPage() {
390
402
  params: { app, action: actionId },
391
403
  search: { ...search, view: v },
392
404
  }), children: _jsx(Icon, { "aria-hidden": true }) }, v));
393
- })] })) : null] }), _jsx(SearchBar, { model: model, viewType: type, spec: spec.data, state: state, onChange: setState, labels: labels }), Renderer === undefined ? (_jsx(Alert, { children: `No renderer for '${served.data.view.component ?? type}'.` })) : (_jsx(Renderer, { served: served.data, where: compiled.where, groupBy: compiled.groupBy, order: compiled.order, onOrder: (order) => setState({ ...state, order }), onOpen: open, onDrill: drill }))] }));
405
+ })] })) : null] }), _jsx(ViewTabs, { model: model, viewType: type, state: state, onChange: setState }), _jsx(SearchBar, { model: model, viewType: type, spec: spec.data, state: state, onChange: setState, labels: labels }), Renderer === undefined ? (_jsx(Alert, { children: `No renderer for '${served.data.view.component ?? type}'.` })) : (_jsx(Renderer, { served: served.data, where: compiled.where, groupBy: compiled.groupBy, order: compiled.order, onOrder: (order) => setState({ ...state, order }), onOpen: open, onDrill: drill }))] }));
394
406
  }
395
407
  /** What the record page needs from the shell: the API, the user, the apps (for breadcrumbs). */
396
408
  function useRecordContext() {
@@ -481,9 +493,24 @@ function ActivatePage() {
481
493
  const navigate = useNavigate();
482
494
  return _jsx(Activate, { api: useServices().api, onSignedIn: () => void navigate({ to: '/' }) });
483
495
  }
496
+ /** Where to go after signing in: `?next=` when it is a path of this site (never another site), else home. */
497
+ function nextPath() {
498
+ const next = new URLSearchParams(window.location.search).get('next') ?? '';
499
+ return next.startsWith('/') && !next.startsWith('//') && !next.startsWith('/\\') ? next : '/';
500
+ }
484
501
  function LoginPage() {
485
502
  const navigate = useNavigate();
486
- return _jsx(Login, { api: useServices().api, onSignedIn: () => void navigate({ to: '/' }) });
503
+ return (_jsx(Login, { api: useServices().api, onSignedIn: () => {
504
+ const next = nextPath();
505
+ // A page outside the router's own paths with its query (the OAuth consent) loads as an address.
506
+ if (next.startsWith('/oauth/'))
507
+ window.location.assign(next);
508
+ else
509
+ void navigate({ to: next });
510
+ } }));
511
+ }
512
+ function ConsentPage() {
513
+ return _jsx(Consent, { api: useServices().api });
487
514
  }
488
515
  // ------------------------------------------------------------------------------------------------- router
489
516
  const rootRoute = createRootRoute({ component: Outlet });
@@ -497,6 +524,12 @@ const activateRoute = createRoute({
497
524
  path: '/activate',
498
525
  component: ActivatePage,
499
526
  });
527
+ /** The OAuth consent page (RFC 0017 §5.2): outside the shell, like sign-in. */
528
+ const consentRoute = createRoute({
529
+ getParentRoute: () => rootRoute,
530
+ path: '/oauth/authorize',
531
+ component: ConsentPage,
532
+ });
500
533
  const shellRoute = createRoute({ getParentRoute: () => rootRoute, id: 'shell', component: Shell });
501
534
  const indexRoute = createRoute({
502
535
  getParentRoute: () => shellRoute,
@@ -546,6 +579,7 @@ const userPageRoute = createRoute({
546
579
  const routeTree = rootRoute.addChildren([
547
580
  loginRoute,
548
581
  activateRoute,
582
+ consentRoute,
549
583
  shellRoute.addChildren([
550
584
  indexRoute,
551
585
  openRoute,
@@ -3,7 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  * App icons by name (`app.icon` in a module manifest): the launcher's tiles and the App manager's cards. Names
4
4
  * follow Lucide; an unknown name shows a neutral grid, never a broken image.
5
5
  */
6
- import { BookOpen, Boxes, Briefcase, ChartColumn, FolderKanban, LayoutGrid, Mail, MessageSquare, MessagesSquare, Package, PencilRuler, Receipt, Ruler, Settings, ShieldCheck, ShoppingCart, Users, Wallet, Warehouse, Workflow, } from 'lucide-react';
6
+ import { BookOpen, Boxes, Briefcase, Building, Building2, CalendarDays, ChartColumn, Factory, FolderKanban, Globe, Landmark, LayoutGrid, ListChecks, LockKeyhole, Mail, MessageSquare, MessagesSquare, Package, PencilRuler, Plug, Receipt, Ruler, ScrollText, Settings, ShieldCheck, ShoppingCart, Sparkles, Store, Ticket, TrendingUp, UserPlus, Users, Wallet, Warehouse, Workflow, Wrench, } from 'lucide-react';
7
7
  import { cn } from '../lib/cn.js';
8
8
  const ICONS = {
9
9
  settings: Settings,
@@ -37,6 +37,38 @@ const ICONS = {
37
37
  shield: ShieldCheck,
38
38
  permissions: ShieldCheck,
39
39
  briefcase: Briefcase,
40
+ plug: Plug,
41
+ focus: ListChecks,
42
+ agenda: CalendarDays,
43
+ pipeline: TrendingUp,
44
+ workforce: Briefcase,
45
+ hiring: UserPlus,
46
+ stockroom: Warehouse,
47
+ production: Factory,
48
+ assetcare: Wrench,
49
+ tradefinance: Landmark,
50
+ eventhub: Ticket,
51
+ sites: Globe,
52
+ counter: Store,
53
+ event: Ticket,
54
+ trade_finance: Landmark,
55
+ maintenance: Wrench,
56
+ mrp: Factory,
57
+ stock: Warehouse,
58
+ recruitment: UserPlus,
59
+ hr: Briefcase,
60
+ crm: TrendingUp,
61
+ calendar: CalendarDays,
62
+ todo: ListChecks,
63
+ integrations: Plug,
64
+ cart: ShoppingCart,
65
+ company: Building2,
66
+ companies: Building,
67
+ security: LockKeyhole,
68
+ audit: ScrollText,
69
+ apps: LayoutGrid,
70
+ assistant: Sparkles,
71
+ sparkles: Sparkles,
40
72
  };
41
73
  /** A tinted square with the app's icon (decorative: the app's name is always shown next to it). */
42
74
  export function AppIcon({ name, className, }) {
@@ -0,0 +1,21 @@
1
+ import { type ReactElement } from 'react';
2
+ /** The URL of a photo (`user`) or logo (`company`), or null when there is none. */
3
+ export declare function imageUrl(kind: 'user' | 'company', id: number, version: string | null | undefined): string | null;
4
+ /** Up to two initials of a name. */
5
+ export declare function initialsOf(name: string): string;
6
+ export declare function Avatar({ name, src, shape, className, }: {
7
+ readonly name: string;
8
+ readonly src: string | null;
9
+ /** People are round; companies square. */
10
+ readonly shape?: 'circle' | 'square';
11
+ readonly className?: string;
12
+ }): ReactElement;
13
+ export declare function ImagePicker({ name, src, fit, onChoose, onRemove, busy, }: {
14
+ readonly name: string;
15
+ readonly src: string | null;
16
+ readonly fit: 'photo' | 'logo';
17
+ /** The image as a `data:` URL, already small. */
18
+ readonly onChoose: (dataUrl: string) => void;
19
+ readonly onRemove: () => void;
20
+ readonly busy?: boolean;
21
+ }): ReactElement;
@@ -0,0 +1,71 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Photos and logos: `Avatar` shows a person's photo or a company's logo (the server's image route, versioned so the
4
+ * browser caches it), else the initials on a tint. `ImagePicker` chooses a file and makes it small in the browser —
5
+ * a 256 px square, cropped to the centre for photos, fitted whole for logos — before anything is sent.
6
+ */
7
+ import { ImageUp, Trash2 } from 'lucide-react';
8
+ import { useId, useRef, useState } from 'react';
9
+ import { useTranslation } from 'react-i18next';
10
+ import { cn } from '../lib/cn.js';
11
+ import { Button } from './primitives.js';
12
+ /** The URL of a photo (`user`) or logo (`company`), or null when there is none. */
13
+ export function imageUrl(kind, id, version) {
14
+ return version === null || version === undefined
15
+ ? null
16
+ : `/api/v1/images/${kind}/${id}?v=${version}`;
17
+ }
18
+ /** Up to two initials of a name. */
19
+ export function initialsOf(name) {
20
+ return name
21
+ .split(/\s+/)
22
+ .filter((w) => w !== '')
23
+ .map((w) => w.slice(0, 1))
24
+ .join('')
25
+ .slice(0, 2)
26
+ .toUpperCase();
27
+ }
28
+ export function Avatar({ name, src, shape = 'circle', className, }) {
29
+ const [failed, setFailed] = useState(false);
30
+ const round = shape === 'circle' ? 'rounded-full' : 'rounded-md';
31
+ if (src !== null && !failed)
32
+ return (_jsx("img", { src: src, alt: "", "aria-hidden": true, onError: () => setFailed(true), className: cn('size-7 shrink-0 bg-surface object-cover', shape === 'square' && 'object-contain', round, className) }));
33
+ return (_jsx("span", { "aria-hidden": true, className: cn('grid size-7 shrink-0 place-items-center bg-primary text-xs font-medium text-primary-foreground', round, className), children: initialsOf(name) }));
34
+ }
35
+ const SIZE = 256;
36
+ /** Draws `file` on a 256 px square: covered and centred (`photo`) or fitted whole (`logo`). */
37
+ async function shrink(file, fit) {
38
+ const bitmap = await createImageBitmap(file);
39
+ const canvas = document.createElement('canvas');
40
+ canvas.width = SIZE;
41
+ canvas.height = SIZE;
42
+ const ctx = canvas.getContext('2d');
43
+ if (ctx === null)
44
+ throw new Error('No canvas');
45
+ const scale = fit === 'photo'
46
+ ? Math.max(SIZE / bitmap.width, SIZE / bitmap.height)
47
+ : Math.min(SIZE / bitmap.width, SIZE / bitmap.height);
48
+ const w = bitmap.width * scale;
49
+ const h = bitmap.height * scale;
50
+ ctx.imageSmoothingQuality = 'high';
51
+ ctx.drawImage(bitmap, (SIZE - w) / 2, (SIZE - h) / 2, w, h);
52
+ bitmap.close();
53
+ // Photos as WebP (small); logos as PNG (they keep transparency and sharp edges).
54
+ return fit === 'photo' ? canvas.toDataURL('image/webp', 0.86) : canvas.toDataURL('image/png');
55
+ }
56
+ export function ImagePicker({ name, src, fit, onChoose, onRemove, busy, }) {
57
+ const { t } = useTranslation();
58
+ const id = useId();
59
+ const input = useRef(null);
60
+ const [error, setError] = useState(null);
61
+ return (_jsxs("div", { className: "flex flex-wrap items-center gap-4", children: [_jsx(Avatar, { name: name, src: src, shape: fit === 'photo' ? 'circle' : 'square', className: "size-16 text-lg" }), _jsxs("div", { className: "grid gap-1.5", children: [_jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx("input", { ref: input, id: id, type: "file", accept: "image/png,image/jpeg,image/webp", className: "sr-only", onChange: (e) => {
62
+ const file = e.target.files?.[0];
63
+ e.target.value = '';
64
+ if (file === undefined)
65
+ return;
66
+ setError(null);
67
+ shrink(file, fit)
68
+ .then(onChoose)
69
+ .catch(() => setError(t('image.unreadable')));
70
+ } }), _jsxs(Button, { size: "sm", busy: busy === true, onClick: () => input.current?.click(), children: [_jsx(ImageUp, { "aria-hidden": true }), fit === 'photo' ? t('image.choosePhoto') : t('image.chooseLogo')] }), src === null ? null : (_jsxs(Button, { size: "sm", variant: "ghost", onClick: onRemove, children: [_jsx(Trash2, { "aria-hidden": true }), t('image.remove')] }))] }), _jsx("p", { className: "text-xs text-muted-foreground", children: error ?? t('image.hint') })] })] }));
71
+ }
@@ -76,3 +76,5 @@ export declare function EmptyState({ icon, title, description, action, }: {
76
76
  readonly description?: string;
77
77
  readonly action?: ReactNode;
78
78
  }): ReactElement;
79
+ /** The classes of a button, for elements that must look like one (a file picker's label). */
80
+ export declare function buttonClass(variant?: ButtonProps['variant'], size?: ButtonProps['size']): string;
@@ -23,7 +23,9 @@ const buttonVariants = cva('inline-flex items-center justify-center gap-2 whites
23
23
  });
24
24
  export function Button({ className, variant, size, asChild, busy, children, disabled, ...props }) {
25
25
  const Comp = asChild === true ? Slot.Root : 'button';
26
- return (_jsxs(Comp, { className: cn(buttonVariants({ variant, size }), className), disabled: disabled === true || busy === true, "aria-busy": busy === true ? true : undefined, ...props, children: [busy === true ? _jsx(Loader2, { className: "animate-spin", "aria-hidden": true }) : null, children] }));
26
+ return (_jsxs(Comp
27
+ // A button inside a record's form must not save it: only `type="submit"` submits.
28
+ , { ...(asChild === true ? {} : { type: 'button' }), className: cn(buttonVariants({ variant, size }), className), disabled: disabled === true || busy === true, "aria-busy": busy === true ? true : undefined, ...props, children: [busy === true ? _jsx(Loader2, { className: "animate-spin", "aria-hidden": true }) : null, children] }));
27
29
  }
28
30
  const field = 'w-full rounded-md border border-input bg-surface px-3 text-sm text-surface-foreground placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-60 read-only:bg-muted aria-invalid:border-danger';
29
31
  export function Input({ className, ...props }) {
@@ -120,3 +122,7 @@ export function Skeleton({ className, ...props }) {
120
122
  export function EmptyState({ icon, title, description, action, }) {
121
123
  return (_jsxs("div", { className: "flex flex-col items-center gap-2 px-6 py-12 text-center", children: [icon === undefined ? null : (_jsx("div", { className: "mb-1 flex size-12 items-center justify-center rounded-full bg-muted text-muted-foreground [&_svg]:size-6", children: icon })), _jsx("p", { className: "font-medium", children: title }), description === undefined ? null : (_jsx("p", { className: "max-w-md text-sm text-muted-foreground", children: description })), action === undefined ? null : _jsx("div", { className: "mt-2", children: action })] }));
122
124
  }
125
+ /** The classes of a button, for elements that must look like one (a file picker's label). */
126
+ export function buttonClass(variant = 'primary', size = 'md') {
127
+ return buttonVariants({ variant, size });
128
+ }
@@ -88,8 +88,12 @@ export declare const en: {
88
88
  export: string;
89
89
  exportCsv: string;
90
90
  exportXlsx: string;
91
+ exportPdf: string;
91
92
  exportFailed: string;
92
93
  columns: string;
94
+ pinned: string;
95
+ resetWidths: string;
96
+ resize: string;
93
97
  density: string;
94
98
  statusOf: string;
95
99
  nextState: string;
@@ -113,6 +117,7 @@ export declare const en: {
113
117
  compact: string;
114
118
  comfortable: string;
115
119
  emptyTitle: string;
120
+ emptyNamed: string;
116
121
  emptyHint: string;
117
122
  emptyFiltered: string;
118
123
  moved: string;
@@ -166,6 +171,23 @@ export declare const en: {
166
171
  none: string;
167
172
  lines: string;
168
173
  addLine: string;
174
+ nextStep: {
175
+ ready: string;
176
+ before: string;
177
+ missing_one: string;
178
+ missing_other: string;
179
+ };
180
+ daysLeft: {
181
+ today: string;
182
+ in: string;
183
+ late: string;
184
+ };
185
+ image: {
186
+ choose: string;
187
+ remove: string;
188
+ none: string;
189
+ saveFirst: string;
190
+ };
169
191
  removeLine: string;
170
192
  choose: string;
171
193
  pasted_one: string;
@@ -234,11 +256,37 @@ export declare const en: {
234
256
  done: string;
235
257
  recent: string;
236
258
  apps: string;
259
+ today: string;
260
+ approve: string;
261
+ review: string;
262
+ dueToday: string;
263
+ todo: string;
264
+ meetings: string;
265
+ mentioned: string;
266
+ saved: string;
237
267
  };
238
268
  toast: {
239
269
  region: string;
240
270
  close: string;
241
271
  };
272
+ image: {
273
+ choosePhoto: string;
274
+ chooseLogo: string;
275
+ remove: string;
276
+ hint: string;
277
+ unreadable: string;
278
+ };
279
+ oauth: {
280
+ title: string;
281
+ as: string;
282
+ may: string;
283
+ limits: string;
284
+ back: string;
285
+ homepage: string;
286
+ already: string;
287
+ allow: string;
288
+ cancel: string;
289
+ };
242
290
  palette: {
243
291
  switchCompany: string;
244
292
  profile: string;
@@ -292,6 +340,11 @@ export declare const en: {
292
340
  custom: string;
293
341
  remove: string;
294
342
  or: string;
343
+ savedViews: string;
344
+ allRecords: string;
345
+ moreViews_one: string;
346
+ moreViews_other: string;
347
+ shared: string;
295
348
  };
296
349
  };
297
350
  type Messages = typeof en;