@cratis/showrunner.boxoffice.react 0.0.0-stage → 1.9.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 (329) hide show
  1. package/Banners/NoLicense.tsx +36 -0
  2. package/Banners/SuspensionBanner.tsx +54 -0
  3. package/Banners/TrialBanner.tsx +30 -0
  4. package/Banners/bannerModels.ts +117 -0
  5. package/Banners/index.ts +7 -0
  6. package/Entitlements/EntitlementsContext.ts +17 -0
  7. package/Entitlements/RequiresModule.tsx +68 -0
  8. package/Entitlements/entitlementModel.ts +175 -0
  9. package/Entitlements/index.ts +9 -0
  10. package/Entitlements/moduleFilter.ts +23 -0
  11. package/Entitlements/requiresModuleModel.ts +44 -0
  12. package/Entitlements/useEntitlements.ts +25 -0
  13. package/Labels/BoxOfficeLabels.ts +239 -0
  14. package/Labels/defaultBoxOfficeLabels.ts +119 -0
  15. package/Labels/format.ts +13 -0
  16. package/Labels/index.ts +7 -0
  17. package/Labels/resolveLabels.ts +36 -0
  18. package/Plan/PlanPage.tsx +24 -0
  19. package/Plan/PlanView.tsx +80 -0
  20. package/Plan/index.ts +6 -0
  21. package/Plan/planModels.ts +127 -0
  22. package/Portal/PortalButton.tsx +48 -0
  23. package/Portal/index.ts +5 -0
  24. package/Portal/portalLinks.ts +32 -0
  25. package/Provider/BoxOfficeContext.ts +67 -0
  26. package/Provider/BoxOfficeProvider.tsx +96 -0
  27. package/Provider/index.ts +5 -0
  28. package/README.md +71 -2
  29. package/Seats/AssignSeatDialog.tsx +73 -0
  30. package/Seats/SeatsPage.tsx +85 -0
  31. package/Seats/SeatsView.tsx +108 -0
  32. package/Seats/index.ts +7 -0
  33. package/Seats/seatModels.ts +97 -0
  34. package/Usage/UsagePage.tsx +26 -0
  35. package/Usage/UsageView.tsx +94 -0
  36. package/Usage/index.ts +6 -0
  37. package/Usage/usageModels.ts +92 -0
  38. package/dist/cjs/Banners/NoLicense.d.ts +2 -0
  39. package/dist/cjs/Banners/NoLicense.d.ts.map +1 -0
  40. package/dist/cjs/Banners/NoLicense.js +50 -0
  41. package/dist/cjs/Banners/NoLicense.js.map +1 -0
  42. package/dist/cjs/Banners/SuspensionBanner.d.ts +7 -0
  43. package/dist/cjs/Banners/SuspensionBanner.d.ts.map +1 -0
  44. package/dist/cjs/Banners/SuspensionBanner.js +62 -0
  45. package/dist/cjs/Banners/SuspensionBanner.js.map +1 -0
  46. package/dist/cjs/Banners/TrialBanner.d.ts +2 -0
  47. package/dist/cjs/Banners/TrialBanner.d.ts.map +1 -0
  48. package/dist/cjs/Banners/TrialBanner.js +38 -0
  49. package/dist/cjs/Banners/TrialBanner.js.map +1 -0
  50. package/dist/cjs/Banners/bannerModels.d.ts +19 -0
  51. package/dist/cjs/Banners/bannerModels.d.ts.map +1 -0
  52. package/dist/cjs/Banners/bannerModels.js +101 -0
  53. package/dist/cjs/Banners/bannerModels.js.map +1 -0
  54. package/dist/cjs/Banners/index.d.ts +5 -0
  55. package/dist/cjs/Banners/index.d.ts.map +1 -0
  56. package/dist/cjs/Entitlements/EntitlementsContext.d.ts +4 -0
  57. package/dist/cjs/Entitlements/EntitlementsContext.d.ts.map +1 -0
  58. package/dist/cjs/Entitlements/EntitlementsContext.js +19 -0
  59. package/dist/cjs/Entitlements/EntitlementsContext.js.map +1 -0
  60. package/dist/cjs/Entitlements/RequiresModule.d.ts +9 -0
  61. package/dist/cjs/Entitlements/RequiresModule.d.ts.map +1 -0
  62. package/dist/cjs/Entitlements/RequiresModule.js +68 -0
  63. package/dist/cjs/Entitlements/RequiresModule.js.map +1 -0
  64. package/dist/cjs/Entitlements/entitlementModel.d.ts +25 -0
  65. package/dist/cjs/Entitlements/entitlementModel.d.ts.map +1 -0
  66. package/dist/cjs/Entitlements/entitlementModel.js +93 -0
  67. package/dist/cjs/Entitlements/entitlementModel.js.map +1 -0
  68. package/dist/cjs/Entitlements/index.d.ts +7 -0
  69. package/dist/cjs/Entitlements/index.d.ts.map +1 -0
  70. package/dist/cjs/Entitlements/moduleFilter.d.ts +6 -0
  71. package/dist/cjs/Entitlements/moduleFilter.d.ts.map +1 -0
  72. package/dist/cjs/Entitlements/moduleFilter.js +16 -0
  73. package/dist/cjs/Entitlements/moduleFilter.js.map +1 -0
  74. package/dist/cjs/Entitlements/requiresModuleModel.d.ts +10 -0
  75. package/dist/cjs/Entitlements/requiresModuleModel.d.ts.map +1 -0
  76. package/dist/cjs/Entitlements/requiresModuleModel.js +32 -0
  77. package/dist/cjs/Entitlements/requiresModuleModel.js.map +1 -0
  78. package/dist/cjs/Entitlements/useEntitlements.d.ts +5 -0
  79. package/dist/cjs/Entitlements/useEntitlements.d.ts.map +1 -0
  80. package/dist/cjs/Entitlements/useEntitlements.js +26 -0
  81. package/dist/cjs/Entitlements/useEntitlements.js.map +1 -0
  82. package/dist/cjs/Labels/BoxOfficeLabels.d.ts +116 -0
  83. package/dist/cjs/Labels/BoxOfficeLabels.d.ts.map +1 -0
  84. package/dist/cjs/Labels/defaultBoxOfficeLabels.d.ts +3 -0
  85. package/dist/cjs/Labels/defaultBoxOfficeLabels.d.ts.map +1 -0
  86. package/dist/cjs/Labels/defaultBoxOfficeLabels.js +120 -0
  87. package/dist/cjs/Labels/defaultBoxOfficeLabels.js.map +1 -0
  88. package/dist/cjs/Labels/format.d.ts +2 -0
  89. package/dist/cjs/Labels/format.d.ts.map +1 -0
  90. package/dist/cjs/Labels/format.js +15 -0
  91. package/dist/cjs/Labels/format.js.map +1 -0
  92. package/dist/cjs/Labels/index.d.ts +5 -0
  93. package/dist/cjs/Labels/index.d.ts.map +1 -0
  94. package/dist/cjs/Labels/resolveLabels.d.ts +3 -0
  95. package/dist/cjs/Labels/resolveLabels.d.ts.map +1 -0
  96. package/dist/cjs/Labels/resolveLabels.js +35 -0
  97. package/dist/cjs/Labels/resolveLabels.js.map +1 -0
  98. package/dist/cjs/Plan/PlanPage.d.ts +2 -0
  99. package/dist/cjs/Plan/PlanPage.d.ts.map +1 -0
  100. package/dist/cjs/Plan/PlanPage.js +24 -0
  101. package/dist/cjs/Plan/PlanPage.js.map +1 -0
  102. package/dist/cjs/Plan/PlanView.d.ts +10 -0
  103. package/dist/cjs/Plan/PlanView.d.ts.map +1 -0
  104. package/dist/cjs/Plan/PlanView.js +123 -0
  105. package/dist/cjs/Plan/PlanView.js.map +1 -0
  106. package/dist/cjs/Plan/index.d.ts +4 -0
  107. package/dist/cjs/Plan/index.d.ts.map +1 -0
  108. package/dist/cjs/Plan/planModels.d.ts +24 -0
  109. package/dist/cjs/Plan/planModels.d.ts.map +1 -0
  110. package/dist/cjs/Plan/planModels.js +83 -0
  111. package/dist/cjs/Plan/planModels.js.map +1 -0
  112. package/dist/cjs/Portal/PortalButton.d.ts +10 -0
  113. package/dist/cjs/Portal/PortalButton.d.ts.map +1 -0
  114. package/dist/cjs/Portal/PortalButton.js +37 -0
  115. package/dist/cjs/Portal/PortalButton.js.map +1 -0
  116. package/dist/cjs/Portal/index.d.ts +3 -0
  117. package/dist/cjs/Portal/index.d.ts.map +1 -0
  118. package/dist/cjs/Portal/portalLinks.d.ts +6 -0
  119. package/dist/cjs/Portal/portalLinks.d.ts.map +1 -0
  120. package/dist/cjs/Portal/portalLinks.js +34 -0
  121. package/dist/cjs/Portal/portalLinks.js.map +1 -0
  122. package/dist/cjs/Provider/BoxOfficeContext.d.ts +16 -0
  123. package/dist/cjs/Provider/BoxOfficeContext.d.ts.map +1 -0
  124. package/dist/cjs/Provider/BoxOfficeContext.js +34 -0
  125. package/dist/cjs/Provider/BoxOfficeContext.js.map +1 -0
  126. package/dist/cjs/Provider/BoxOfficeProvider.d.ts +15 -0
  127. package/dist/cjs/Provider/BoxOfficeProvider.d.ts.map +1 -0
  128. package/dist/cjs/Provider/BoxOfficeProvider.js +72 -0
  129. package/dist/cjs/Provider/BoxOfficeProvider.js.map +1 -0
  130. package/dist/cjs/Provider/index.d.ts +3 -0
  131. package/dist/cjs/Provider/index.d.ts.map +1 -0
  132. package/dist/cjs/Seats/AssignSeatDialog.d.ts +9 -0
  133. package/dist/cjs/Seats/AssignSeatDialog.d.ts.map +1 -0
  134. package/dist/cjs/Seats/AssignSeatDialog.js +99 -0
  135. package/dist/cjs/Seats/AssignSeatDialog.js.map +1 -0
  136. package/dist/cjs/Seats/SeatsPage.d.ts +2 -0
  137. package/dist/cjs/Seats/SeatsPage.d.ts.map +1 -0
  138. package/dist/cjs/Seats/SeatsPage.js +87 -0
  139. package/dist/cjs/Seats/SeatsPage.js.map +1 -0
  140. package/dist/cjs/Seats/SeatsView.d.ts +14 -0
  141. package/dist/cjs/Seats/SeatsView.d.ts.map +1 -0
  142. package/dist/cjs/Seats/SeatsView.js +111 -0
  143. package/dist/cjs/Seats/SeatsView.js.map +1 -0
  144. package/dist/cjs/Seats/index.d.ts +5 -0
  145. package/dist/cjs/Seats/index.d.ts.map +1 -0
  146. package/dist/cjs/Seats/seatModels.d.ts +23 -0
  147. package/dist/cjs/Seats/seatModels.d.ts.map +1 -0
  148. package/dist/cjs/Seats/seatModels.js +65 -0
  149. package/dist/cjs/Seats/seatModels.js.map +1 -0
  150. package/dist/cjs/Usage/UsagePage.d.ts +2 -0
  151. package/dist/cjs/Usage/UsagePage.d.ts.map +1 -0
  152. package/dist/cjs/Usage/UsagePage.js +26 -0
  153. package/dist/cjs/Usage/UsagePage.js.map +1 -0
  154. package/dist/cjs/Usage/UsageView.d.ts +9 -0
  155. package/dist/cjs/Usage/UsageView.d.ts.map +1 -0
  156. package/dist/cjs/Usage/UsageView.js +108 -0
  157. package/dist/cjs/Usage/UsageView.js.map +1 -0
  158. package/dist/cjs/Usage/index.d.ts +4 -0
  159. package/dist/cjs/Usage/index.d.ts.map +1 -0
  160. package/dist/cjs/Usage/usageModels.d.ts +17 -0
  161. package/dist/cjs/Usage/usageModels.d.ts.map +1 -0
  162. package/dist/cjs/Usage/usageModels.js +57 -0
  163. package/dist/cjs/Usage/usageModels.js.map +1 -0
  164. package/dist/cjs/formatting.d.ts +5 -0
  165. package/dist/cjs/formatting.d.ts.map +1 -0
  166. package/dist/cjs/formatting.js +46 -0
  167. package/dist/cjs/formatting.js.map +1 -0
  168. package/dist/cjs/index.d.ts +10 -0
  169. package/dist/cjs/index.d.ts.map +1 -0
  170. package/dist/cjs/index.js +85 -0
  171. package/dist/cjs/index.js.map +1 -0
  172. package/dist/cjs/package.json +3 -0
  173. package/dist/esm/Banners/NoLicense.d.ts +2 -0
  174. package/dist/esm/Banners/NoLicense.d.ts.map +1 -0
  175. package/dist/esm/Banners/NoLicense.js +48 -0
  176. package/dist/esm/Banners/NoLicense.js.map +1 -0
  177. package/dist/esm/Banners/SuspensionBanner.d.ts +7 -0
  178. package/dist/esm/Banners/SuspensionBanner.d.ts.map +1 -0
  179. package/dist/esm/Banners/SuspensionBanner.js +59 -0
  180. package/dist/esm/Banners/SuspensionBanner.js.map +1 -0
  181. package/dist/esm/Banners/TrialBanner.d.ts +2 -0
  182. package/dist/esm/Banners/TrialBanner.d.ts.map +1 -0
  183. package/dist/esm/Banners/TrialBanner.js +36 -0
  184. package/dist/esm/Banners/TrialBanner.js.map +1 -0
  185. package/dist/esm/Banners/bannerModels.d.ts +19 -0
  186. package/dist/esm/Banners/bannerModels.d.ts.map +1 -0
  187. package/dist/esm/Banners/bannerModels.js +96 -0
  188. package/dist/esm/Banners/bannerModels.js.map +1 -0
  189. package/dist/esm/Banners/index.d.ts +5 -0
  190. package/dist/esm/Banners/index.d.ts.map +1 -0
  191. package/dist/esm/Banners/index.js +5 -0
  192. package/dist/esm/Banners/index.js.map +1 -0
  193. package/dist/esm/Entitlements/EntitlementsContext.d.ts +4 -0
  194. package/dist/esm/Entitlements/EntitlementsContext.d.ts.map +1 -0
  195. package/dist/esm/Entitlements/EntitlementsContext.js +16 -0
  196. package/dist/esm/Entitlements/EntitlementsContext.js.map +1 -0
  197. package/dist/esm/Entitlements/RequiresModule.d.ts +9 -0
  198. package/dist/esm/Entitlements/RequiresModule.d.ts.map +1 -0
  199. package/dist/esm/Entitlements/RequiresModule.js +66 -0
  200. package/dist/esm/Entitlements/RequiresModule.js.map +1 -0
  201. package/dist/esm/Entitlements/entitlementModel.d.ts +25 -0
  202. package/dist/esm/Entitlements/entitlementModel.d.ts.map +1 -0
  203. package/dist/esm/Entitlements/entitlementModel.js +89 -0
  204. package/dist/esm/Entitlements/entitlementModel.js.map +1 -0
  205. package/dist/esm/Entitlements/index.d.ts +7 -0
  206. package/dist/esm/Entitlements/index.d.ts.map +1 -0
  207. package/dist/esm/Entitlements/index.js +7 -0
  208. package/dist/esm/Entitlements/index.js.map +1 -0
  209. package/dist/esm/Entitlements/moduleFilter.d.ts +6 -0
  210. package/dist/esm/Entitlements/moduleFilter.d.ts.map +1 -0
  211. package/dist/esm/Entitlements/moduleFilter.js +14 -0
  212. package/dist/esm/Entitlements/moduleFilter.js.map +1 -0
  213. package/dist/esm/Entitlements/requiresModuleModel.d.ts +10 -0
  214. package/dist/esm/Entitlements/requiresModuleModel.d.ts.map +1 -0
  215. package/dist/esm/Entitlements/requiresModuleModel.js +30 -0
  216. package/dist/esm/Entitlements/requiresModuleModel.js.map +1 -0
  217. package/dist/esm/Entitlements/useEntitlements.d.ts +5 -0
  218. package/dist/esm/Entitlements/useEntitlements.d.ts.map +1 -0
  219. package/dist/esm/Entitlements/useEntitlements.js +23 -0
  220. package/dist/esm/Entitlements/useEntitlements.js.map +1 -0
  221. package/dist/esm/Labels/BoxOfficeLabels.d.ts +116 -0
  222. package/dist/esm/Labels/BoxOfficeLabels.d.ts.map +1 -0
  223. package/dist/esm/Labels/BoxOfficeLabels.js +2 -0
  224. package/dist/esm/Labels/BoxOfficeLabels.js.map +1 -0
  225. package/dist/esm/Labels/defaultBoxOfficeLabels.d.ts +3 -0
  226. package/dist/esm/Labels/defaultBoxOfficeLabels.d.ts.map +1 -0
  227. package/dist/esm/Labels/defaultBoxOfficeLabels.js +118 -0
  228. package/dist/esm/Labels/defaultBoxOfficeLabels.js.map +1 -0
  229. package/dist/esm/Labels/format.d.ts +2 -0
  230. package/dist/esm/Labels/format.d.ts.map +1 -0
  231. package/dist/esm/Labels/format.js +13 -0
  232. package/dist/esm/Labels/format.js.map +1 -0
  233. package/dist/esm/Labels/index.d.ts +5 -0
  234. package/dist/esm/Labels/index.d.ts.map +1 -0
  235. package/dist/esm/Labels/index.js +5 -0
  236. package/dist/esm/Labels/index.js.map +1 -0
  237. package/dist/esm/Labels/resolveLabels.d.ts +3 -0
  238. package/dist/esm/Labels/resolveLabels.d.ts.map +1 -0
  239. package/dist/esm/Labels/resolveLabels.js +33 -0
  240. package/dist/esm/Labels/resolveLabels.js.map +1 -0
  241. package/dist/esm/Plan/PlanPage.d.ts +2 -0
  242. package/dist/esm/Plan/PlanPage.d.ts.map +1 -0
  243. package/dist/esm/Plan/PlanPage.js +22 -0
  244. package/dist/esm/Plan/PlanPage.js.map +1 -0
  245. package/dist/esm/Plan/PlanView.d.ts +10 -0
  246. package/dist/esm/Plan/PlanView.d.ts.map +1 -0
  247. package/dist/esm/Plan/PlanView.js +121 -0
  248. package/dist/esm/Plan/PlanView.js.map +1 -0
  249. package/dist/esm/Plan/index.d.ts +4 -0
  250. package/dist/esm/Plan/index.d.ts.map +1 -0
  251. package/dist/esm/Plan/index.js +4 -0
  252. package/dist/esm/Plan/index.js.map +1 -0
  253. package/dist/esm/Plan/planModels.d.ts +24 -0
  254. package/dist/esm/Plan/planModels.d.ts.map +1 -0
  255. package/dist/esm/Plan/planModels.js +78 -0
  256. package/dist/esm/Plan/planModels.js.map +1 -0
  257. package/dist/esm/Portal/PortalButton.d.ts +10 -0
  258. package/dist/esm/Portal/PortalButton.d.ts.map +1 -0
  259. package/dist/esm/Portal/PortalButton.js +35 -0
  260. package/dist/esm/Portal/PortalButton.js.map +1 -0
  261. package/dist/esm/Portal/index.d.ts +3 -0
  262. package/dist/esm/Portal/index.d.ts.map +1 -0
  263. package/dist/esm/Portal/index.js +3 -0
  264. package/dist/esm/Portal/index.js.map +1 -0
  265. package/dist/esm/Portal/portalLinks.d.ts +6 -0
  266. package/dist/esm/Portal/portalLinks.d.ts.map +1 -0
  267. package/dist/esm/Portal/portalLinks.js +31 -0
  268. package/dist/esm/Portal/portalLinks.js.map +1 -0
  269. package/dist/esm/Provider/BoxOfficeContext.d.ts +16 -0
  270. package/dist/esm/Provider/BoxOfficeContext.d.ts.map +1 -0
  271. package/dist/esm/Provider/BoxOfficeContext.js +29 -0
  272. package/dist/esm/Provider/BoxOfficeContext.js.map +1 -0
  273. package/dist/esm/Provider/BoxOfficeProvider.d.ts +15 -0
  274. package/dist/esm/Provider/BoxOfficeProvider.d.ts.map +1 -0
  275. package/dist/esm/Provider/BoxOfficeProvider.js +70 -0
  276. package/dist/esm/Provider/BoxOfficeProvider.js.map +1 -0
  277. package/dist/esm/Provider/index.d.ts +3 -0
  278. package/dist/esm/Provider/index.d.ts.map +1 -0
  279. package/dist/esm/Provider/index.js +3 -0
  280. package/dist/esm/Provider/index.js.map +1 -0
  281. package/dist/esm/Seats/AssignSeatDialog.d.ts +9 -0
  282. package/dist/esm/Seats/AssignSeatDialog.d.ts.map +1 -0
  283. package/dist/esm/Seats/AssignSeatDialog.js +97 -0
  284. package/dist/esm/Seats/AssignSeatDialog.js.map +1 -0
  285. package/dist/esm/Seats/SeatsPage.d.ts +2 -0
  286. package/dist/esm/Seats/SeatsPage.d.ts.map +1 -0
  287. package/dist/esm/Seats/SeatsPage.js +85 -0
  288. package/dist/esm/Seats/SeatsPage.js.map +1 -0
  289. package/dist/esm/Seats/SeatsView.d.ts +14 -0
  290. package/dist/esm/Seats/SeatsView.d.ts.map +1 -0
  291. package/dist/esm/Seats/SeatsView.js +109 -0
  292. package/dist/esm/Seats/SeatsView.js.map +1 -0
  293. package/dist/esm/Seats/index.d.ts +5 -0
  294. package/dist/esm/Seats/index.d.ts.map +1 -0
  295. package/dist/esm/Seats/index.js +5 -0
  296. package/dist/esm/Seats/index.js.map +1 -0
  297. package/dist/esm/Seats/seatModels.d.ts +23 -0
  298. package/dist/esm/Seats/seatModels.d.ts.map +1 -0
  299. package/dist/esm/Seats/seatModels.js +59 -0
  300. package/dist/esm/Seats/seatModels.js.map +1 -0
  301. package/dist/esm/Usage/UsagePage.d.ts +2 -0
  302. package/dist/esm/Usage/UsagePage.d.ts.map +1 -0
  303. package/dist/esm/Usage/UsagePage.js +24 -0
  304. package/dist/esm/Usage/UsagePage.js.map +1 -0
  305. package/dist/esm/Usage/UsageView.d.ts +9 -0
  306. package/dist/esm/Usage/UsageView.d.ts.map +1 -0
  307. package/dist/esm/Usage/UsageView.js +106 -0
  308. package/dist/esm/Usage/UsageView.js.map +1 -0
  309. package/dist/esm/Usage/index.d.ts +4 -0
  310. package/dist/esm/Usage/index.d.ts.map +1 -0
  311. package/dist/esm/Usage/index.js +4 -0
  312. package/dist/esm/Usage/index.js.map +1 -0
  313. package/dist/esm/Usage/usageModels.d.ts +17 -0
  314. package/dist/esm/Usage/usageModels.d.ts.map +1 -0
  315. package/dist/esm/Usage/usageModels.js +53 -0
  316. package/dist/esm/Usage/usageModels.js.map +1 -0
  317. package/dist/esm/formatting.d.ts +5 -0
  318. package/dist/esm/formatting.d.ts.map +1 -0
  319. package/dist/esm/formatting.js +42 -0
  320. package/dist/esm/formatting.js.map +1 -0
  321. package/dist/esm/index.d.ts +10 -0
  322. package/dist/esm/index.d.ts.map +1 -0
  323. package/dist/esm/index.js +29 -0
  324. package/dist/esm/index.js.map +1 -0
  325. package/dist/esm/package.json +3 -0
  326. package/formatting.ts +41 -0
  327. package/index.ts +12 -0
  328. package/package.json +84 -4
  329. package/styles.css +166 -0
@@ -0,0 +1,239 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ /**
5
+ * Every text the Box Office components show or announce. Placeholders in braces, such as `{days}`, are filled in by
6
+ * the components.
7
+ */
8
+ export interface BoxOfficeLabels {
9
+ /** Texts shared by several components. */
10
+ common: {
11
+ /** The cancel button. */
12
+ cancel: string;
13
+ /** The accessible name of a dialog's close button. */
14
+ close: string;
15
+ /** Shown while something loads. */
16
+ loading: string;
17
+ /** Shown when something could not be loaded. */
18
+ failed: string;
19
+ /** Shown when the user may not see something. */
20
+ unauthorized: string;
21
+ /** Shown when the customer portal could not be opened. */
22
+ portalFailed: string;
23
+ /** Shown instead of a portal button when the product has no customer portal. */
24
+ noPortal: string;
25
+ };
26
+
27
+ /** What a module's access is called. */
28
+ moduleAccess: {
29
+ /** The tenant has no access. */
30
+ none: string;
31
+ /** The tenant may look at the module but not use it. */
32
+ preview: string;
33
+ /** The tenant has the module on trial. */
34
+ trial: string;
35
+ /** The tenant has the module. */
36
+ full: string;
37
+ /** Says when access ends. `{date}` is the last day. */
38
+ until: string;
39
+ };
40
+
41
+ /** The banner that follows a trial. */
42
+ trialBanner: {
43
+ /** A trial that runs. `{days}` is how many days are left. */
44
+ running: string;
45
+ /** A trial that ends today. */
46
+ lastDay: string;
47
+ /** A trial that has closed. */
48
+ closed: string;
49
+ /** The button that opens the customer portal to buy. */
50
+ buy: string;
51
+ };
52
+
53
+ /** The banner for a suspended tenant. */
54
+ suspension: {
55
+ /** The title. */
56
+ title: string;
57
+ /** Why, when the trial ended without seats. */
58
+ trialEndedWithoutSeats: string;
59
+ /** Why, when a payment is overdue. */
60
+ paymentOverdue: string;
61
+ /** Why, when an administrator decided it. */
62
+ administratorDecision: string;
63
+ /** What to do about it. */
64
+ resolve: string;
65
+ /** The button that opens the customer portal. */
66
+ openPortal: string;
67
+ };
68
+
69
+ /** What a user without a license is told. */
70
+ noLicense: {
71
+ /** The title when the user has no seat. */
72
+ noSeatTitle: string;
73
+ /** What to do when the user has no seat. */
74
+ noSeat: string;
75
+ /** The title when more people hold seats than the tenant has. */
76
+ overCapacityTitle: string;
77
+ /** What to do when more people hold seats than the tenant has. */
78
+ overCapacity: string;
79
+ /** The title when the trial has closed. */
80
+ trialClosedTitle: string;
81
+ /** What to do when the trial has closed. */
82
+ trialClosed: string;
83
+ /** The title when the tenant is suspended. */
84
+ suspendedTitle: string;
85
+ /** What to do when the tenant is suspended. */
86
+ suspended: string;
87
+ /** The button that opens the customer portal. */
88
+ openPortal: string;
89
+ };
90
+
91
+ /** What the user sees where a module they do not have would be. */
92
+ requiresModule: {
93
+ /** The title. `{module}` is the module. */
94
+ title: string;
95
+ /** The explanation when the tenant has no access. */
96
+ none: string;
97
+ /** The explanation when the tenant may only preview the module. */
98
+ preview: string;
99
+ /** The button that opens the customer portal to try the module. */
100
+ try: string;
101
+ /** The button that opens the customer portal to buy the module. */
102
+ buy: string;
103
+ };
104
+
105
+ /** The seats page. */
106
+ seats: {
107
+ /** The title. */
108
+ title: string;
109
+ /** How many seats are used. `{held}` and `{capacity}` are the numbers. */
110
+ summary: string;
111
+ /** Shown when more people hold seats than the tenant has. `{over}` is how many are over. */
112
+ overCapacity: string;
113
+ /** Shown when the tenant has no seats at all. */
114
+ noCapacity: string;
115
+ /** Shown when nobody holds a seat. */
116
+ empty: string;
117
+ /** The column of names. */
118
+ holderColumn: string;
119
+ /** The column of email addresses. */
120
+ emailColumn: string;
121
+ /** The column of when a seat was assigned. */
122
+ assignedColumn: string;
123
+ /** The column of seat status. */
124
+ statusColumn: string;
125
+ /** A seat that is licensed. */
126
+ licensed: string;
127
+ /** A seat the tenant has no room for. */
128
+ overCapacityStatus: string;
129
+ /** The button that assigns a seat. */
130
+ assign: string;
131
+ /** The text of the button that releases a seat. */
132
+ releaseShort: string;
133
+ /** The accessible name of the button that releases a seat. `{name}` is the person. */
134
+ release: string;
135
+ /** The title of the assign dialog. */
136
+ assignTitle: string;
137
+ /** The assign dialog's confirm button. */
138
+ assignConfirm: string;
139
+ /** The person's name field. */
140
+ name: string;
141
+ /** The person's email field. */
142
+ email: string;
143
+ /** The identity provider field. */
144
+ identityProvider: string;
145
+ /** The person's subject at the identity provider. */
146
+ subject: string;
147
+ /** The title of the confirmation to release a seat. */
148
+ releaseTitle: string;
149
+ /** The message of the confirmation to release a seat. `{name}` is the person. */
150
+ releaseMessage: string;
151
+ /** Said when seats cannot be changed because the tenant is read-only. */
152
+ readOnly: string;
153
+ };
154
+
155
+ /** The usage page. */
156
+ usage: {
157
+ /** The title. */
158
+ title: string;
159
+ /** Shown when the product measures nothing. */
160
+ empty: string;
161
+ /** The column of meters. */
162
+ meterColumn: string;
163
+ /** The column of what is used. */
164
+ usedColumn: string;
165
+ /** The column of the allowance. */
166
+ allowanceColumn: string;
167
+ /** The column of the budget. */
168
+ budgetColumn: string;
169
+ /** The column of concurrency limits. */
170
+ concurrencyColumn: string;
171
+ /** How much is used of the allowance. `{used}`, `{allowance}` and `{unit}` are the values. */
172
+ usedOfAllowance: string;
173
+ /** Said when the operator set no allowance. `{used}` and `{unit}` are the values. */
174
+ noAllowance: string;
175
+ /** The period of an allowance that counts per day. */
176
+ day: string;
177
+ /** The period of an allowance that counts per month. */
178
+ month: string;
179
+ /** Says when the period starts over. `{date}` is the day. */
180
+ resets: string;
181
+ /** Said when the allowance is used up. */
182
+ atAllowance: string;
183
+ /** Said when the budget is reached. */
184
+ atBudget: string;
185
+ /** Said of work beyond the allowance that is turned away. */
186
+ block: string;
187
+ /** Said of work beyond the allowance that waits for the next period. */
188
+ park: string;
189
+ /** Shown for a budget or limit nobody set. */
190
+ notSet: string;
191
+ /** The accessible name of a meter's progress bar. `{meter}` is the meter. */
192
+ progress: string;
193
+ };
194
+
195
+ /** The plan and modules page. */
196
+ plan: {
197
+ /** The title. */
198
+ title: string;
199
+ /** The heading of the modules. */
200
+ modulesTitle: string;
201
+ /** The heading of the offers. */
202
+ offersTitle: string;
203
+ /** Shown when no module is declared. */
204
+ noModules: string;
205
+ /** Shown when nothing is on sale. */
206
+ noOffers: string;
207
+ /** The seats an offer includes. `{count}` is the number. */
208
+ includedSeats: string;
209
+ /** The modules an offer includes. */
210
+ includedModules: string;
211
+ /** A price per month. `{price}` is the amount with its currency. */
212
+ perMonth: string;
213
+ /** A price per year. `{price}` is the amount with its currency. */
214
+ perYear: string;
215
+ /** The button that opens the customer portal to buy an offer. */
216
+ buy: string;
217
+ /** The button that opens the customer portal to try a module. */
218
+ try: string;
219
+ /** The button that opens the customer portal to change plan. */
220
+ changePlan: string;
221
+ /** Shown for a tenant on a trial. `{days}` is how many days are left. */
222
+ onTrial: string;
223
+ /** Shown for a tenant with seats. `{seats}` is how many. */
224
+ withSeats: string;
225
+ };
226
+ }
227
+
228
+ /**
229
+ * Makes every property of a type optional, all the way down.
230
+ */
231
+ export type DeepPartial<T> = {
232
+ [TKey in keyof T]?: T[TKey] extends object ? DeepPartial<T[TKey]> : T[TKey];
233
+ };
234
+
235
+ /**
236
+ * The labels of the Box Office components that a solution replaces, for instance to localize them. Labels left out
237
+ * keep their English defaults.
238
+ */
239
+ export type BoxOfficeLabelOverrides = DeepPartial<BoxOfficeLabels>;
@@ -0,0 +1,119 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { BoxOfficeLabels } from './BoxOfficeLabels.js';
5
+
6
+ /**
7
+ * The English labels of the Box Office components.
8
+ */
9
+ export const defaultBoxOfficeLabels: BoxOfficeLabels = {
10
+ common: {
11
+ cancel: 'Cancel',
12
+ close: 'Close',
13
+ loading: 'Loading…',
14
+ failed: 'This could not be loaded.',
15
+ unauthorized: 'You are not allowed to see this.',
16
+ portalFailed: 'The customer portal could not be opened. Try again in a moment.',
17
+ noPortal: 'Contact the people who run this product to change your plan.'
18
+ },
19
+ moduleAccess: {
20
+ none: 'Not included',
21
+ preview: 'Preview',
22
+ trial: 'Trial',
23
+ full: 'Included',
24
+ until: 'until {date}'
25
+ },
26
+ trialBanner: {
27
+ running: 'Your trial ends in {days} days.',
28
+ lastDay: 'Your trial ends today.',
29
+ closed: 'Your trial has ended. Your data is kept, and you can read it, but nothing can be changed until you buy.',
30
+ buy: 'Buy'
31
+ },
32
+ suspension: {
33
+ title: 'Your organization is suspended',
34
+ trialEndedWithoutSeats: 'Your trial ended without any seats being bought.',
35
+ paymentOverdue: 'A payment is overdue.',
36
+ administratorDecision: 'The people who run this product suspended it.',
37
+ resolve: 'You can read everything, but nothing can be changed until the suspension is lifted.',
38
+ openPortal: 'Open the customer portal'
39
+ },
40
+ noLicense: {
41
+ noSeatTitle: 'You do not have a seat',
42
+ noSeat: 'Ask an administrator of your organization to give you a seat.',
43
+ overCapacityTitle: 'Your seat is not covered',
44
+ overCapacity: 'Your organization has more people with seats than it has bought. Ask an administrator to buy more seats or release some.',
45
+ trialClosedTitle: 'The trial has ended',
46
+ trialClosed: 'Your organization can read what it has, but nothing can be changed until it buys.',
47
+ suspendedTitle: 'Your organization is suspended',
48
+ suspended: 'Your organization can read what it has, but nothing can be changed until the suspension is lifted.',
49
+ openPortal: 'Open the customer portal'
50
+ },
51
+ requiresModule: {
52
+ title: '{module} is not part of your plan',
53
+ none: 'Your organization does not have {module}.',
54
+ preview: 'Your organization can look at {module}, but not use it yet.',
55
+ try: 'Try it',
56
+ buy: 'Buy'
57
+ },
58
+ seats: {
59
+ title: 'Seats',
60
+ summary: '{held} of {capacity} seats in use',
61
+ overCapacity: 'Your organization has {over} more people with seats than it has bought.',
62
+ noCapacity: 'Your organization has no seats.',
63
+ empty: 'Nobody holds a seat yet.',
64
+ holderColumn: 'Name',
65
+ emailColumn: 'Email',
66
+ assignedColumn: 'Assigned',
67
+ statusColumn: 'Status',
68
+ licensed: 'Licensed',
69
+ overCapacityStatus: 'Not covered',
70
+ assign: 'Assign a seat',
71
+ releaseShort: 'Release',
72
+ release: 'Release the seat of {name}',
73
+ assignTitle: 'Assign a seat',
74
+ assignConfirm: 'Assign',
75
+ name: 'Name',
76
+ email: 'Email',
77
+ identityProvider: 'Identity provider',
78
+ subject: 'Subject at the identity provider',
79
+ releaseTitle: 'Release the seat',
80
+ releaseMessage: '{name} can no longer use the product until they are given a seat again. Release the seat?',
81
+ readOnly: 'Seats cannot be changed while your organization is read-only.'
82
+ },
83
+ usage: {
84
+ title: 'Usage',
85
+ empty: 'This product measures nothing.',
86
+ meterColumn: 'Meter',
87
+ usedColumn: 'Used',
88
+ allowanceColumn: 'Allowance',
89
+ budgetColumn: 'Budget',
90
+ concurrencyColumn: 'At the same time',
91
+ usedOfAllowance: '{used} of {allowance} {unit}',
92
+ noAllowance: '{used} {unit}, with no allowance',
93
+ day: 'per day',
94
+ month: 'per month',
95
+ resets: 'starts over {date}',
96
+ atAllowance: 'Allowance used up',
97
+ atBudget: 'Budget reached',
98
+ block: 'Work beyond the allowance is turned away.',
99
+ park: 'Work beyond the allowance waits for the next period.',
100
+ notSet: 'Not set',
101
+ progress: 'Use of {meter}'
102
+ },
103
+ plan: {
104
+ title: 'Plan and modules',
105
+ modulesTitle: 'Modules',
106
+ offersTitle: 'Plans and offers',
107
+ noModules: 'This product has no modules.',
108
+ noOffers: 'Nothing is on sale right now.',
109
+ includedSeats: '{count} seats included',
110
+ includedModules: 'Includes',
111
+ perMonth: '{price} per month',
112
+ perYear: '{price} per year',
113
+ buy: 'Buy',
114
+ try: 'Try',
115
+ changePlan: 'Change plan',
116
+ onTrial: 'You are on a trial with {days} days left.',
117
+ withSeats: 'Your plan has {seats} seats.'
118
+ }
119
+ };
@@ -0,0 +1,13 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ /**
5
+ * Fills the placeholders of a label, such as `{count}`, with values. A placeholder without a value is left as it is.
6
+ * @param template The label.
7
+ * @param values The values by placeholder name.
8
+ * @returns The label with its placeholders filled in.
9
+ */
10
+ export function format(template: string, values: Record<string, string | number>): string {
11
+ return template.replace(/\{(\w+)\}/g, (placeholder, name: string) =>
12
+ Object.hasOwn(values, name) ? String(values[name]) : placeholder);
13
+ }
@@ -0,0 +1,7 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ export * from './BoxOfficeLabels.js';
5
+ export * from './defaultBoxOfficeLabels.js';
6
+ export * from './resolveLabels.js';
7
+ export * from './format.js';
@@ -0,0 +1,36 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { defaultBoxOfficeLabels } from './defaultBoxOfficeLabels.js';
5
+ import { BoxOfficeLabelOverrides, BoxOfficeLabels } from './BoxOfficeLabels.js';
6
+
7
+ /**
8
+ * Resolves the labels to show: the overrides given, and the English default for every label left out.
9
+ * @param overrides The labels a solution replaces.
10
+ * @returns The {@link BoxOfficeLabels}.
11
+ */
12
+ export function resolveLabels(overrides?: BoxOfficeLabelOverrides): BoxOfficeLabels {
13
+ return merge(defaultBoxOfficeLabels, overrides) as BoxOfficeLabels;
14
+ }
15
+
16
+ function merge(defaults: object, overrides?: object): object {
17
+ if (!overrides) {
18
+ return defaults;
19
+ }
20
+
21
+ const merged: Record<string, unknown> = { ...defaults };
22
+ for (const [key, value] of Object.entries(overrides)) {
23
+ if (value === undefined) {
24
+ continue;
25
+ }
26
+
27
+ const defaultValue = (defaults as Record<string, unknown>)[key];
28
+ merged[key] = isObject(defaultValue) && isObject(value) ? merge(defaultValue, value) : value;
29
+ }
30
+
31
+ return merged;
32
+ }
33
+
34
+ function isObject(value: unknown): value is object {
35
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
36
+ }
@@ -0,0 +1,24 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { OnSale } from '@cratis/showrunner.boxoffice';
5
+ import { useEntitlementsContext } from '../Entitlements/EntitlementsContext.js';
6
+ import { PlanView } from './PlanView.js';
7
+
8
+ /**
9
+ * The plan and modules page: where the tenant stands with its plan, which modules it has, and the offers the operator
10
+ * sells, with buttons that open the customer portal to buy, try or change plan. Render it inside a
11
+ * {@link BoxOfficeProvider}.
12
+ */
13
+ export const PlanPage = () => {
14
+ const entitlements = useEntitlementsContext();
15
+ const [offers] = OnSale.use();
16
+
17
+ return (
18
+ <PlanView
19
+ entitlements={entitlements}
20
+ offers={offers.isSuccess ? (offers.data ?? []) : []}
21
+ isLoading={offers.isPerforming && !offers.isSuccess}
22
+ hasFailed={!offers.isPerforming && !offers.isSuccess} />
23
+ );
24
+ };
@@ -0,0 +1,80 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { Message, Tag } from '@cratis/components/Display';
5
+ import { Offer, PortalIntent } from '@cratis/showrunner.boxoffice';
6
+ import { Entitlements } from '../Entitlements/entitlementModel.js';
7
+ import { PortalButton } from '../Portal/PortalButton.js';
8
+ import { useBoxOfficeContext } from '../Provider/BoxOfficeContext.js';
9
+ import { moduleRows, offerRows, planSummary } from './planModels.js';
10
+
11
+ /**
12
+ * Props for {@link PlanView}.
13
+ */
14
+ export interface PlanViewProps {
15
+ /** What the tenant may do. */
16
+ entitlements: Entitlements;
17
+
18
+ /** The offers on sale. */
19
+ offers: readonly Offer[];
20
+
21
+ /** Whether the offers are still loading. */
22
+ isLoading?: boolean;
23
+
24
+ /** Whether the offers could not be loaded. */
25
+ hasFailed?: boolean;
26
+ }
27
+
28
+ /**
29
+ * The plan and modules of the tenant: where it stands, which modules it has, and what it can buy, with buttons that
30
+ * open the customer portal.
31
+ */
32
+ export const PlanView = ({ entitlements, offers, isLoading, hasFailed }: PlanViewProps) => {
33
+ const { labels, locale } = useBoxOfficeContext();
34
+ const modules = moduleRows(entitlements, labels, locale);
35
+ const offerList = offerRows(offers, entitlements, labels, locale);
36
+ const summary = planSummary(entitlements, labels);
37
+
38
+ return (
39
+ <section className='boxoffice-plan' aria-labelledby='boxoffice-plan-title'>
40
+ <header className='boxoffice-page__header'>
41
+ <h2 id='boxoffice-plan-title' className='boxoffice-page__title'>{labels.plan.title}</h2>
42
+ </header>
43
+ {summary && <p className='boxoffice-plan__summary'>{summary}</p>}
44
+ {!entitlements.isLoading && !entitlements.canOpenPortal && <Message severity='info'>{labels.common.noPortal}</Message>}
45
+
46
+ <h3 className='boxoffice-plan__heading'>{labels.plan.modulesTitle}</h3>
47
+ {modules.length === 0
48
+ ? <p className='boxoffice-plan__empty'>{entitlements.isLoading ? labels.common.loading : labels.plan.noModules}</p>
49
+ : (
50
+ <ul className='boxoffice-plan__modules'>
51
+ {modules.map(module => (
52
+ <li key={module.standing.module} className='boxoffice-plan__module'>
53
+ <span className='boxoffice-plan__name'>{module.standing.name}</span>
54
+ <Tag severity={module.severity} value={module.accessText} />
55
+ {module.untilText && <span className='boxoffice-plan__until'>{module.untilText}</span>}
56
+ {module.canTry && <PortalButton intent={PortalIntent.try} label={labels.plan.try} variant='outline' size='small' />}
57
+ </li>
58
+ ))}
59
+ </ul>
60
+ )}
61
+
62
+ <h3 className='boxoffice-plan__heading'>{labels.plan.offersTitle}</h3>
63
+ {hasFailed && <Message severity='error'>{labels.common.failed}</Message>}
64
+ {!hasFailed && offerList.length === 0 && <p className='boxoffice-plan__empty'>{isLoading ? labels.common.loading : labels.plan.noOffers}</p>}
65
+ <ul className='boxoffice-plan__offers'>
66
+ {offerList.map(row => (
67
+ <li key={row.offer.id.toString()} className='boxoffice-plan__offer'>
68
+ <div className='boxoffice-plan__offer-text'>
69
+ <strong>{row.offer.name}</strong>
70
+ {row.seatsText && <span>{row.seatsText}</span>}
71
+ {row.moduleNames.length > 0 && <span>{labels.plan.includedModules}: {row.moduleNames.join(', ')}</span>}
72
+ {row.prices.map(price => <span key={price} className='boxoffice-plan__price'>{price}</span>)}
73
+ </div>
74
+ <PortalButton intent={row.intent} offer={row.offer.id} label={row.buttonLabel} />
75
+ </li>
76
+ ))}
77
+ </ul>
78
+ </section>
79
+ );
80
+ };
package/Plan/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ export * from './planModels.js';
5
+ export * from './PlanPage.js';
6
+ export * from './PlanView.js';
@@ -0,0 +1,127 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { BillingInterval, ModuleAccess, ModuleStanding, Offer, OfferPrice, PortalIntent } from '@cratis/showrunner.boxoffice';
5
+ import { Entitlements } from '../Entitlements/entitlementModel.js';
6
+ import { formatDay, formatMoney } from '../formatting.js';
7
+ import { BoxOfficeLabels, format } from '../Labels/index.js';
8
+
9
+ /** How a module's access is shown. */
10
+ export type AccessSeverity = 'success' | 'info' | 'warn' | 'secondary';
11
+
12
+ /**
13
+ * What the plan page shows for one module.
14
+ */
15
+ export interface ModuleRowModel {
16
+ /** The module's standing. */
17
+ readonly standing: ModuleStanding;
18
+
19
+ /** What the access is called. */
20
+ readonly accessText: string;
21
+
22
+ /** When the access ends, in words; empty when it does not. */
23
+ readonly untilText: string;
24
+
25
+ /** How the access is shown. */
26
+ readonly severity: AccessSeverity;
27
+
28
+ /** Whether to offer to try the module. */
29
+ readonly canTry: boolean;
30
+ }
31
+
32
+ /**
33
+ * What the plan page shows for one offer.
34
+ */
35
+ export interface OfferRowModel {
36
+ /** The offer. */
37
+ readonly offer: Offer;
38
+
39
+ /** The prices, in words, monthly first. */
40
+ readonly prices: readonly string[];
41
+
42
+ /** The seats the offer includes, in words; empty when it includes none. */
43
+ readonly seatsText: string;
44
+
45
+ /** The names of the modules the offer includes. */
46
+ readonly moduleNames: readonly string[];
47
+
48
+ /** What the offer's button opens the customer portal for. */
49
+ readonly intent: PortalIntent;
50
+
51
+ /** The text of the offer's button. */
52
+ readonly buttonLabel: string;
53
+ }
54
+
55
+ /**
56
+ * Lays out the modules of the plan page.
57
+ * @param entitlements What the tenant may do.
58
+ * @param labels The labels to show.
59
+ * @param locale The locale to format for; the browser's when not given.
60
+ * @returns A row for every module the product declares.
61
+ */
62
+ export function moduleRows(entitlements: Entitlements, labels: BoxOfficeLabels, locale?: string): ModuleRowModel[] {
63
+ return entitlements.modules.map(standing => {
64
+ const access = standing.access;
65
+ return {
66
+ standing,
67
+ accessText: access === ModuleAccess.full
68
+ ? labels.moduleAccess.full
69
+ : access === ModuleAccess.trial
70
+ ? labels.moduleAccess.trial
71
+ : access === ModuleAccess.preview ? labels.moduleAccess.preview : labels.moduleAccess.none,
72
+ untilText: standing.until && access !== ModuleAccess.none
73
+ ? format(labels.moduleAccess.until, { date: formatDay(standing.until, locale) })
74
+ : '',
75
+ severity: access === ModuleAccess.full ? 'success' : access === ModuleAccess.trial ? 'info' : access === ModuleAccess.preview ? 'warn' : 'secondary',
76
+ canTry: access === ModuleAccess.none || access === ModuleAccess.preview
77
+ };
78
+ });
79
+ }
80
+
81
+ /**
82
+ * Says what an offer's price is.
83
+ * @param price The price.
84
+ * @param labels The labels to show.
85
+ * @param locale The locale to format for; the browser's when not given.
86
+ * @returns The price, such as "$12.00 per month".
87
+ */
88
+ export function priceText(price: OfferPrice, labels: BoxOfficeLabels, locale?: string): string {
89
+ const money = formatMoney(price.amount, price.currency, locale);
90
+ return format(price.interval === BillingInterval.yearly ? labels.plan.perYear : labels.plan.perMonth, { price: money });
91
+ }
92
+
93
+ /**
94
+ * Lays out the offers of the plan page.
95
+ * @param offers The offers on sale.
96
+ * @param entitlements What the tenant may do.
97
+ * @param labels The labels to show.
98
+ * @param locale The locale to format for; the browser's when not given.
99
+ * @returns A row for every offer.
100
+ */
101
+ export function offerRows(offers: readonly Offer[], entitlements: Entitlements, labels: BoxOfficeLabels, locale?: string): OfferRowModel[] {
102
+ const changes = entitlements.state === 'licensed';
103
+ return offers.map(offer => ({
104
+ offer,
105
+ prices: [...offer.prices]
106
+ .sort((a, b) => a.interval - b.interval)
107
+ .map(price => priceText(price, labels, locale)),
108
+ seatsText: offer.includedSeats > 0 ? format(labels.plan.includedSeats, { count: offer.includedSeats }) : '',
109
+ moduleNames: offer.modules.map(module => entitlements.standingOf(module)?.name ?? module),
110
+ intent: changes ? PortalIntent.changePlan : PortalIntent.buy,
111
+ buttonLabel: changes ? labels.plan.changePlan : labels.plan.buy
112
+ }));
113
+ }
114
+
115
+ /**
116
+ * Says where the tenant stands with its plan.
117
+ * @param entitlements What the tenant may do.
118
+ * @param labels The labels to show.
119
+ * @returns A sentence; empty when there is nothing to say.
120
+ */
121
+ export function planSummary(entitlements: Entitlements, labels: BoxOfficeLabels): string {
122
+ if (entitlements.state === 'trial' && entitlements.trial) {
123
+ return format(labels.plan.onTrial, { days: Math.max(entitlements.trial.daysLeft, 0) });
124
+ }
125
+
126
+ return entitlements.seats > 0 ? format(labels.plan.withSeats, { seats: entitlements.seats }) : '';
127
+ }