@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.
- package/Banners/NoLicense.tsx +36 -0
- package/Banners/SuspensionBanner.tsx +54 -0
- package/Banners/TrialBanner.tsx +30 -0
- package/Banners/bannerModels.ts +117 -0
- package/Banners/index.ts +7 -0
- package/Entitlements/EntitlementsContext.ts +17 -0
- package/Entitlements/RequiresModule.tsx +68 -0
- package/Entitlements/entitlementModel.ts +175 -0
- package/Entitlements/index.ts +9 -0
- package/Entitlements/moduleFilter.ts +23 -0
- package/Entitlements/requiresModuleModel.ts +44 -0
- package/Entitlements/useEntitlements.ts +25 -0
- package/Labels/BoxOfficeLabels.ts +239 -0
- package/Labels/defaultBoxOfficeLabels.ts +119 -0
- package/Labels/format.ts +13 -0
- package/Labels/index.ts +7 -0
- package/Labels/resolveLabels.ts +36 -0
- package/Plan/PlanPage.tsx +24 -0
- package/Plan/PlanView.tsx +80 -0
- package/Plan/index.ts +6 -0
- package/Plan/planModels.ts +127 -0
- package/Portal/PortalButton.tsx +48 -0
- package/Portal/index.ts +5 -0
- package/Portal/portalLinks.ts +32 -0
- package/Provider/BoxOfficeContext.ts +67 -0
- package/Provider/BoxOfficeProvider.tsx +96 -0
- package/Provider/index.ts +5 -0
- package/README.md +71 -2
- package/Seats/AssignSeatDialog.tsx +73 -0
- package/Seats/SeatsPage.tsx +85 -0
- package/Seats/SeatsView.tsx +108 -0
- package/Seats/index.ts +7 -0
- package/Seats/seatModels.ts +97 -0
- package/Usage/UsagePage.tsx +26 -0
- package/Usage/UsageView.tsx +94 -0
- package/Usage/index.ts +6 -0
- package/Usage/usageModels.ts +92 -0
- package/dist/cjs/Banners/NoLicense.d.ts +2 -0
- package/dist/cjs/Banners/NoLicense.d.ts.map +1 -0
- package/dist/cjs/Banners/NoLicense.js +50 -0
- package/dist/cjs/Banners/NoLicense.js.map +1 -0
- package/dist/cjs/Banners/SuspensionBanner.d.ts +7 -0
- package/dist/cjs/Banners/SuspensionBanner.d.ts.map +1 -0
- package/dist/cjs/Banners/SuspensionBanner.js +62 -0
- package/dist/cjs/Banners/SuspensionBanner.js.map +1 -0
- package/dist/cjs/Banners/TrialBanner.d.ts +2 -0
- package/dist/cjs/Banners/TrialBanner.d.ts.map +1 -0
- package/dist/cjs/Banners/TrialBanner.js +38 -0
- package/dist/cjs/Banners/TrialBanner.js.map +1 -0
- package/dist/cjs/Banners/bannerModels.d.ts +19 -0
- package/dist/cjs/Banners/bannerModels.d.ts.map +1 -0
- package/dist/cjs/Banners/bannerModels.js +101 -0
- package/dist/cjs/Banners/bannerModels.js.map +1 -0
- package/dist/cjs/Banners/index.d.ts +5 -0
- package/dist/cjs/Banners/index.d.ts.map +1 -0
- package/dist/cjs/Entitlements/EntitlementsContext.d.ts +4 -0
- package/dist/cjs/Entitlements/EntitlementsContext.d.ts.map +1 -0
- package/dist/cjs/Entitlements/EntitlementsContext.js +19 -0
- package/dist/cjs/Entitlements/EntitlementsContext.js.map +1 -0
- package/dist/cjs/Entitlements/RequiresModule.d.ts +9 -0
- package/dist/cjs/Entitlements/RequiresModule.d.ts.map +1 -0
- package/dist/cjs/Entitlements/RequiresModule.js +68 -0
- package/dist/cjs/Entitlements/RequiresModule.js.map +1 -0
- package/dist/cjs/Entitlements/entitlementModel.d.ts +25 -0
- package/dist/cjs/Entitlements/entitlementModel.d.ts.map +1 -0
- package/dist/cjs/Entitlements/entitlementModel.js +93 -0
- package/dist/cjs/Entitlements/entitlementModel.js.map +1 -0
- package/dist/cjs/Entitlements/index.d.ts +7 -0
- package/dist/cjs/Entitlements/index.d.ts.map +1 -0
- package/dist/cjs/Entitlements/moduleFilter.d.ts +6 -0
- package/dist/cjs/Entitlements/moduleFilter.d.ts.map +1 -0
- package/dist/cjs/Entitlements/moduleFilter.js +16 -0
- package/dist/cjs/Entitlements/moduleFilter.js.map +1 -0
- package/dist/cjs/Entitlements/requiresModuleModel.d.ts +10 -0
- package/dist/cjs/Entitlements/requiresModuleModel.d.ts.map +1 -0
- package/dist/cjs/Entitlements/requiresModuleModel.js +32 -0
- package/dist/cjs/Entitlements/requiresModuleModel.js.map +1 -0
- package/dist/cjs/Entitlements/useEntitlements.d.ts +5 -0
- package/dist/cjs/Entitlements/useEntitlements.d.ts.map +1 -0
- package/dist/cjs/Entitlements/useEntitlements.js +26 -0
- package/dist/cjs/Entitlements/useEntitlements.js.map +1 -0
- package/dist/cjs/Labels/BoxOfficeLabels.d.ts +116 -0
- package/dist/cjs/Labels/BoxOfficeLabels.d.ts.map +1 -0
- package/dist/cjs/Labels/defaultBoxOfficeLabels.d.ts +3 -0
- package/dist/cjs/Labels/defaultBoxOfficeLabels.d.ts.map +1 -0
- package/dist/cjs/Labels/defaultBoxOfficeLabels.js +120 -0
- package/dist/cjs/Labels/defaultBoxOfficeLabels.js.map +1 -0
- package/dist/cjs/Labels/format.d.ts +2 -0
- package/dist/cjs/Labels/format.d.ts.map +1 -0
- package/dist/cjs/Labels/format.js +15 -0
- package/dist/cjs/Labels/format.js.map +1 -0
- package/dist/cjs/Labels/index.d.ts +5 -0
- package/dist/cjs/Labels/index.d.ts.map +1 -0
- package/dist/cjs/Labels/resolveLabels.d.ts +3 -0
- package/dist/cjs/Labels/resolveLabels.d.ts.map +1 -0
- package/dist/cjs/Labels/resolveLabels.js +35 -0
- package/dist/cjs/Labels/resolveLabels.js.map +1 -0
- package/dist/cjs/Plan/PlanPage.d.ts +2 -0
- package/dist/cjs/Plan/PlanPage.d.ts.map +1 -0
- package/dist/cjs/Plan/PlanPage.js +24 -0
- package/dist/cjs/Plan/PlanPage.js.map +1 -0
- package/dist/cjs/Plan/PlanView.d.ts +10 -0
- package/dist/cjs/Plan/PlanView.d.ts.map +1 -0
- package/dist/cjs/Plan/PlanView.js +123 -0
- package/dist/cjs/Plan/PlanView.js.map +1 -0
- package/dist/cjs/Plan/index.d.ts +4 -0
- package/dist/cjs/Plan/index.d.ts.map +1 -0
- package/dist/cjs/Plan/planModels.d.ts +24 -0
- package/dist/cjs/Plan/planModels.d.ts.map +1 -0
- package/dist/cjs/Plan/planModels.js +83 -0
- package/dist/cjs/Plan/planModels.js.map +1 -0
- package/dist/cjs/Portal/PortalButton.d.ts +10 -0
- package/dist/cjs/Portal/PortalButton.d.ts.map +1 -0
- package/dist/cjs/Portal/PortalButton.js +37 -0
- package/dist/cjs/Portal/PortalButton.js.map +1 -0
- package/dist/cjs/Portal/index.d.ts +3 -0
- package/dist/cjs/Portal/index.d.ts.map +1 -0
- package/dist/cjs/Portal/portalLinks.d.ts +6 -0
- package/dist/cjs/Portal/portalLinks.d.ts.map +1 -0
- package/dist/cjs/Portal/portalLinks.js +34 -0
- package/dist/cjs/Portal/portalLinks.js.map +1 -0
- package/dist/cjs/Provider/BoxOfficeContext.d.ts +16 -0
- package/dist/cjs/Provider/BoxOfficeContext.d.ts.map +1 -0
- package/dist/cjs/Provider/BoxOfficeContext.js +34 -0
- package/dist/cjs/Provider/BoxOfficeContext.js.map +1 -0
- package/dist/cjs/Provider/BoxOfficeProvider.d.ts +15 -0
- package/dist/cjs/Provider/BoxOfficeProvider.d.ts.map +1 -0
- package/dist/cjs/Provider/BoxOfficeProvider.js +72 -0
- package/dist/cjs/Provider/BoxOfficeProvider.js.map +1 -0
- package/dist/cjs/Provider/index.d.ts +3 -0
- package/dist/cjs/Provider/index.d.ts.map +1 -0
- package/dist/cjs/Seats/AssignSeatDialog.d.ts +9 -0
- package/dist/cjs/Seats/AssignSeatDialog.d.ts.map +1 -0
- package/dist/cjs/Seats/AssignSeatDialog.js +99 -0
- package/dist/cjs/Seats/AssignSeatDialog.js.map +1 -0
- package/dist/cjs/Seats/SeatsPage.d.ts +2 -0
- package/dist/cjs/Seats/SeatsPage.d.ts.map +1 -0
- package/dist/cjs/Seats/SeatsPage.js +87 -0
- package/dist/cjs/Seats/SeatsPage.js.map +1 -0
- package/dist/cjs/Seats/SeatsView.d.ts +14 -0
- package/dist/cjs/Seats/SeatsView.d.ts.map +1 -0
- package/dist/cjs/Seats/SeatsView.js +111 -0
- package/dist/cjs/Seats/SeatsView.js.map +1 -0
- package/dist/cjs/Seats/index.d.ts +5 -0
- package/dist/cjs/Seats/index.d.ts.map +1 -0
- package/dist/cjs/Seats/seatModels.d.ts +23 -0
- package/dist/cjs/Seats/seatModels.d.ts.map +1 -0
- package/dist/cjs/Seats/seatModels.js +65 -0
- package/dist/cjs/Seats/seatModels.js.map +1 -0
- package/dist/cjs/Usage/UsagePage.d.ts +2 -0
- package/dist/cjs/Usage/UsagePage.d.ts.map +1 -0
- package/dist/cjs/Usage/UsagePage.js +26 -0
- package/dist/cjs/Usage/UsagePage.js.map +1 -0
- package/dist/cjs/Usage/UsageView.d.ts +9 -0
- package/dist/cjs/Usage/UsageView.d.ts.map +1 -0
- package/dist/cjs/Usage/UsageView.js +108 -0
- package/dist/cjs/Usage/UsageView.js.map +1 -0
- package/dist/cjs/Usage/index.d.ts +4 -0
- package/dist/cjs/Usage/index.d.ts.map +1 -0
- package/dist/cjs/Usage/usageModels.d.ts +17 -0
- package/dist/cjs/Usage/usageModels.d.ts.map +1 -0
- package/dist/cjs/Usage/usageModels.js +57 -0
- package/dist/cjs/Usage/usageModels.js.map +1 -0
- package/dist/cjs/formatting.d.ts +5 -0
- package/dist/cjs/formatting.d.ts.map +1 -0
- package/dist/cjs/formatting.js +46 -0
- package/dist/cjs/formatting.js.map +1 -0
- package/dist/cjs/index.d.ts +10 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +85 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/esm/Banners/NoLicense.d.ts +2 -0
- package/dist/esm/Banners/NoLicense.d.ts.map +1 -0
- package/dist/esm/Banners/NoLicense.js +48 -0
- package/dist/esm/Banners/NoLicense.js.map +1 -0
- package/dist/esm/Banners/SuspensionBanner.d.ts +7 -0
- package/dist/esm/Banners/SuspensionBanner.d.ts.map +1 -0
- package/dist/esm/Banners/SuspensionBanner.js +59 -0
- package/dist/esm/Banners/SuspensionBanner.js.map +1 -0
- package/dist/esm/Banners/TrialBanner.d.ts +2 -0
- package/dist/esm/Banners/TrialBanner.d.ts.map +1 -0
- package/dist/esm/Banners/TrialBanner.js +36 -0
- package/dist/esm/Banners/TrialBanner.js.map +1 -0
- package/dist/esm/Banners/bannerModels.d.ts +19 -0
- package/dist/esm/Banners/bannerModels.d.ts.map +1 -0
- package/dist/esm/Banners/bannerModels.js +96 -0
- package/dist/esm/Banners/bannerModels.js.map +1 -0
- package/dist/esm/Banners/index.d.ts +5 -0
- package/dist/esm/Banners/index.d.ts.map +1 -0
- package/dist/esm/Banners/index.js +5 -0
- package/dist/esm/Banners/index.js.map +1 -0
- package/dist/esm/Entitlements/EntitlementsContext.d.ts +4 -0
- package/dist/esm/Entitlements/EntitlementsContext.d.ts.map +1 -0
- package/dist/esm/Entitlements/EntitlementsContext.js +16 -0
- package/dist/esm/Entitlements/EntitlementsContext.js.map +1 -0
- package/dist/esm/Entitlements/RequiresModule.d.ts +9 -0
- package/dist/esm/Entitlements/RequiresModule.d.ts.map +1 -0
- package/dist/esm/Entitlements/RequiresModule.js +66 -0
- package/dist/esm/Entitlements/RequiresModule.js.map +1 -0
- package/dist/esm/Entitlements/entitlementModel.d.ts +25 -0
- package/dist/esm/Entitlements/entitlementModel.d.ts.map +1 -0
- package/dist/esm/Entitlements/entitlementModel.js +89 -0
- package/dist/esm/Entitlements/entitlementModel.js.map +1 -0
- package/dist/esm/Entitlements/index.d.ts +7 -0
- package/dist/esm/Entitlements/index.d.ts.map +1 -0
- package/dist/esm/Entitlements/index.js +7 -0
- package/dist/esm/Entitlements/index.js.map +1 -0
- package/dist/esm/Entitlements/moduleFilter.d.ts +6 -0
- package/dist/esm/Entitlements/moduleFilter.d.ts.map +1 -0
- package/dist/esm/Entitlements/moduleFilter.js +14 -0
- package/dist/esm/Entitlements/moduleFilter.js.map +1 -0
- package/dist/esm/Entitlements/requiresModuleModel.d.ts +10 -0
- package/dist/esm/Entitlements/requiresModuleModel.d.ts.map +1 -0
- package/dist/esm/Entitlements/requiresModuleModel.js +30 -0
- package/dist/esm/Entitlements/requiresModuleModel.js.map +1 -0
- package/dist/esm/Entitlements/useEntitlements.d.ts +5 -0
- package/dist/esm/Entitlements/useEntitlements.d.ts.map +1 -0
- package/dist/esm/Entitlements/useEntitlements.js +23 -0
- package/dist/esm/Entitlements/useEntitlements.js.map +1 -0
- package/dist/esm/Labels/BoxOfficeLabels.d.ts +116 -0
- package/dist/esm/Labels/BoxOfficeLabels.d.ts.map +1 -0
- package/dist/esm/Labels/BoxOfficeLabels.js +2 -0
- package/dist/esm/Labels/BoxOfficeLabels.js.map +1 -0
- package/dist/esm/Labels/defaultBoxOfficeLabels.d.ts +3 -0
- package/dist/esm/Labels/defaultBoxOfficeLabels.d.ts.map +1 -0
- package/dist/esm/Labels/defaultBoxOfficeLabels.js +118 -0
- package/dist/esm/Labels/defaultBoxOfficeLabels.js.map +1 -0
- package/dist/esm/Labels/format.d.ts +2 -0
- package/dist/esm/Labels/format.d.ts.map +1 -0
- package/dist/esm/Labels/format.js +13 -0
- package/dist/esm/Labels/format.js.map +1 -0
- package/dist/esm/Labels/index.d.ts +5 -0
- package/dist/esm/Labels/index.d.ts.map +1 -0
- package/dist/esm/Labels/index.js +5 -0
- package/dist/esm/Labels/index.js.map +1 -0
- package/dist/esm/Labels/resolveLabels.d.ts +3 -0
- package/dist/esm/Labels/resolveLabels.d.ts.map +1 -0
- package/dist/esm/Labels/resolveLabels.js +33 -0
- package/dist/esm/Labels/resolveLabels.js.map +1 -0
- package/dist/esm/Plan/PlanPage.d.ts +2 -0
- package/dist/esm/Plan/PlanPage.d.ts.map +1 -0
- package/dist/esm/Plan/PlanPage.js +22 -0
- package/dist/esm/Plan/PlanPage.js.map +1 -0
- package/dist/esm/Plan/PlanView.d.ts +10 -0
- package/dist/esm/Plan/PlanView.d.ts.map +1 -0
- package/dist/esm/Plan/PlanView.js +121 -0
- package/dist/esm/Plan/PlanView.js.map +1 -0
- package/dist/esm/Plan/index.d.ts +4 -0
- package/dist/esm/Plan/index.d.ts.map +1 -0
- package/dist/esm/Plan/index.js +4 -0
- package/dist/esm/Plan/index.js.map +1 -0
- package/dist/esm/Plan/planModels.d.ts +24 -0
- package/dist/esm/Plan/planModels.d.ts.map +1 -0
- package/dist/esm/Plan/planModels.js +78 -0
- package/dist/esm/Plan/planModels.js.map +1 -0
- package/dist/esm/Portal/PortalButton.d.ts +10 -0
- package/dist/esm/Portal/PortalButton.d.ts.map +1 -0
- package/dist/esm/Portal/PortalButton.js +35 -0
- package/dist/esm/Portal/PortalButton.js.map +1 -0
- package/dist/esm/Portal/index.d.ts +3 -0
- package/dist/esm/Portal/index.d.ts.map +1 -0
- package/dist/esm/Portal/index.js +3 -0
- package/dist/esm/Portal/index.js.map +1 -0
- package/dist/esm/Portal/portalLinks.d.ts +6 -0
- package/dist/esm/Portal/portalLinks.d.ts.map +1 -0
- package/dist/esm/Portal/portalLinks.js +31 -0
- package/dist/esm/Portal/portalLinks.js.map +1 -0
- package/dist/esm/Provider/BoxOfficeContext.d.ts +16 -0
- package/dist/esm/Provider/BoxOfficeContext.d.ts.map +1 -0
- package/dist/esm/Provider/BoxOfficeContext.js +29 -0
- package/dist/esm/Provider/BoxOfficeContext.js.map +1 -0
- package/dist/esm/Provider/BoxOfficeProvider.d.ts +15 -0
- package/dist/esm/Provider/BoxOfficeProvider.d.ts.map +1 -0
- package/dist/esm/Provider/BoxOfficeProvider.js +70 -0
- package/dist/esm/Provider/BoxOfficeProvider.js.map +1 -0
- package/dist/esm/Provider/index.d.ts +3 -0
- package/dist/esm/Provider/index.d.ts.map +1 -0
- package/dist/esm/Provider/index.js +3 -0
- package/dist/esm/Provider/index.js.map +1 -0
- package/dist/esm/Seats/AssignSeatDialog.d.ts +9 -0
- package/dist/esm/Seats/AssignSeatDialog.d.ts.map +1 -0
- package/dist/esm/Seats/AssignSeatDialog.js +97 -0
- package/dist/esm/Seats/AssignSeatDialog.js.map +1 -0
- package/dist/esm/Seats/SeatsPage.d.ts +2 -0
- package/dist/esm/Seats/SeatsPage.d.ts.map +1 -0
- package/dist/esm/Seats/SeatsPage.js +85 -0
- package/dist/esm/Seats/SeatsPage.js.map +1 -0
- package/dist/esm/Seats/SeatsView.d.ts +14 -0
- package/dist/esm/Seats/SeatsView.d.ts.map +1 -0
- package/dist/esm/Seats/SeatsView.js +109 -0
- package/dist/esm/Seats/SeatsView.js.map +1 -0
- package/dist/esm/Seats/index.d.ts +5 -0
- package/dist/esm/Seats/index.d.ts.map +1 -0
- package/dist/esm/Seats/index.js +5 -0
- package/dist/esm/Seats/index.js.map +1 -0
- package/dist/esm/Seats/seatModels.d.ts +23 -0
- package/dist/esm/Seats/seatModels.d.ts.map +1 -0
- package/dist/esm/Seats/seatModels.js +59 -0
- package/dist/esm/Seats/seatModels.js.map +1 -0
- package/dist/esm/Usage/UsagePage.d.ts +2 -0
- package/dist/esm/Usage/UsagePage.d.ts.map +1 -0
- package/dist/esm/Usage/UsagePage.js +24 -0
- package/dist/esm/Usage/UsagePage.js.map +1 -0
- package/dist/esm/Usage/UsageView.d.ts +9 -0
- package/dist/esm/Usage/UsageView.d.ts.map +1 -0
- package/dist/esm/Usage/UsageView.js +106 -0
- package/dist/esm/Usage/UsageView.js.map +1 -0
- package/dist/esm/Usage/index.d.ts +4 -0
- package/dist/esm/Usage/index.d.ts.map +1 -0
- package/dist/esm/Usage/index.js +4 -0
- package/dist/esm/Usage/index.js.map +1 -0
- package/dist/esm/Usage/usageModels.d.ts +17 -0
- package/dist/esm/Usage/usageModels.d.ts.map +1 -0
- package/dist/esm/Usage/usageModels.js +53 -0
- package/dist/esm/Usage/usageModels.js.map +1 -0
- package/dist/esm/formatting.d.ts +5 -0
- package/dist/esm/formatting.d.ts.map +1 -0
- package/dist/esm/formatting.js +42 -0
- package/dist/esm/formatting.js.map +1 -0
- package/dist/esm/index.d.ts +10 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +29 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/formatting.ts +41 -0
- package/index.ts +12 -0
- package/package.json +84 -4
- package/styles.css +166 -0
|
@@ -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 { Message } from '@cratis/components/Display';
|
|
5
|
+
import { PortalIntent } from '@cratis/showrunner.boxoffice';
|
|
6
|
+
import { useEntitlementsContext } from '../Entitlements/EntitlementsContext.js';
|
|
7
|
+
import { PortalButton } from '../Portal/PortalButton.js';
|
|
8
|
+
import { useBoxOfficeContext } from '../Provider/BoxOfficeContext.js';
|
|
9
|
+
import { noLicenseModel } from './bannerModels.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Tells a user who cannot use the product why, and what to do about it: the tenant is suspended, its trial ended, the
|
|
13
|
+
* user has no seat, or the tenant has more people with seats than it has bought. Nothing is shown to a user with a
|
|
14
|
+
* license. Render it where the application's content would be, or above it.
|
|
15
|
+
*/
|
|
16
|
+
export const NoLicense = () => {
|
|
17
|
+
const entitlements = useEntitlementsContext();
|
|
18
|
+
const { labels, seat } = useBoxOfficeContext();
|
|
19
|
+
const model = noLicenseModel(entitlements, seat, labels);
|
|
20
|
+
|
|
21
|
+
if (!model) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div className='boxoffice-no-license' data-reason={model.reason}>
|
|
27
|
+
<Message severity={model.reason === 'noSeat' || model.reason === 'overCapacity' ? 'warn' : 'error'}>
|
|
28
|
+
<div className='boxoffice-banner__text'>
|
|
29
|
+
<strong>{model.title}</strong>
|
|
30
|
+
<span>{model.text}</span>
|
|
31
|
+
</div>
|
|
32
|
+
</Message>
|
|
33
|
+
{model.reason !== 'noSeat' && <PortalButton intent={PortalIntent.buy} label={labels.noLicense.openPortal} variant='outline' />}
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
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 } from '@cratis/components/Display';
|
|
5
|
+
import { CurrentStanding, PortalIntent, ReadOnlyReason, SuspensionCause } from '@cratis/showrunner.boxoffice';
|
|
6
|
+
import { useEntitlementsContext } from '../Entitlements/EntitlementsContext.js';
|
|
7
|
+
import { PortalButton } from '../Portal/PortalButton.js';
|
|
8
|
+
import { useBoxOfficeLabels } from '../Provider/BoxOfficeContext.js';
|
|
9
|
+
import { suspensionReason } from './bannerModels.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Props for {@link SuspensionBannerView}.
|
|
13
|
+
*/
|
|
14
|
+
export interface SuspensionBannerViewProps {
|
|
15
|
+
/** The cause the operator gave; the reason is left out when not known. */
|
|
16
|
+
cause?: SuspensionCause;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* What the suspension banner shows, for the cause it is given.
|
|
21
|
+
*/
|
|
22
|
+
export const SuspensionBannerView = ({ cause }: SuspensionBannerViewProps) => {
|
|
23
|
+
const labels = useBoxOfficeLabels();
|
|
24
|
+
const reason = suspensionReason(cause, labels);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className='boxoffice-banner'>
|
|
28
|
+
<Message severity='error'>
|
|
29
|
+
<div className='boxoffice-banner__text'>
|
|
30
|
+
<strong>{labels.suspension.title}</strong>
|
|
31
|
+
{reason && <span>{reason}</span>}
|
|
32
|
+
<span>{labels.suspension.resolve}</span>
|
|
33
|
+
</div>
|
|
34
|
+
</Message>
|
|
35
|
+
<PortalButton intent={PortalIntent.buy} label={labels.suspension.openPortal} size='small' variant='outline' />
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Tells the user that the operator suspended the tenant and why, and that everything is read-only until it is lifted.
|
|
42
|
+
* Nothing is shown when the tenant is not suspended. Render it once, above the application's pages.
|
|
43
|
+
*/
|
|
44
|
+
export const SuspensionBanner = () => {
|
|
45
|
+
const entitlements = useEntitlementsContext();
|
|
46
|
+
const isSuspended = entitlements.readOnlyReason === ReadOnlyReason.tenantSuspended;
|
|
47
|
+
const [standing] = CurrentStanding.when(isSuspended).use();
|
|
48
|
+
|
|
49
|
+
if (!isSuspended) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return <SuspensionBannerView cause={standing.isSuccess ? standing.data?.cause : undefined} />;
|
|
54
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
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 } from '@cratis/components/Display';
|
|
5
|
+
import { PortalIntent } from '@cratis/showrunner.boxoffice';
|
|
6
|
+
import { useEntitlementsContext } from '../Entitlements/EntitlementsContext.js';
|
|
7
|
+
import { PortalButton } from '../Portal/PortalButton.js';
|
|
8
|
+
import { useBoxOfficeLabels } from '../Provider/BoxOfficeContext.js';
|
|
9
|
+
import { trialBannerModel } from './bannerModels.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Tells the user how long the tenant's trial runs, or that it has closed, with a button to buy. Nothing is shown when
|
|
13
|
+
* the tenant is not on a trial. Render it once, above the application's pages.
|
|
14
|
+
*/
|
|
15
|
+
export const TrialBanner = () => {
|
|
16
|
+
const entitlements = useEntitlementsContext();
|
|
17
|
+
const labels = useBoxOfficeLabels();
|
|
18
|
+
const model = trialBannerModel(entitlements, labels);
|
|
19
|
+
|
|
20
|
+
if (!model) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div className='boxoffice-banner'>
|
|
26
|
+
<Message severity={model.severity}>{model.text}</Message>
|
|
27
|
+
<PortalButton intent={PortalIntent.buy} label={labels.trialBanner.buy} size='small' />
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
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 { ReadOnlyReason, SeatStanding, SeatStatus, SuspensionCause } from '@cratis/showrunner.boxoffice';
|
|
5
|
+
import { Entitlements } from '../Entitlements/entitlementModel.js';
|
|
6
|
+
import { BoxOfficeLabels, format } from '../Labels/index.js';
|
|
7
|
+
|
|
8
|
+
/** How loud a banner is. */
|
|
9
|
+
export type BannerSeverity = 'info' | 'warn' | 'error';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* What the trial banner says.
|
|
13
|
+
*/
|
|
14
|
+
export interface TrialBannerModel {
|
|
15
|
+
/** How loud the banner is. */
|
|
16
|
+
readonly severity: BannerSeverity;
|
|
17
|
+
|
|
18
|
+
/** What it says. */
|
|
19
|
+
readonly text: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** How many days before a trial ends the banner turns into a warning. */
|
|
23
|
+
export const trialWarningDays = 7;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Works out what the trial banner says.
|
|
27
|
+
* @param entitlements What the tenant may do.
|
|
28
|
+
* @param labels The labels to show.
|
|
29
|
+
* @returns What to say; undefined when the tenant is not on a trial, or the trial is not what makes it read-only.
|
|
30
|
+
*/
|
|
31
|
+
export function trialBannerModel(entitlements: Entitlements, labels: BoxOfficeLabels): TrialBannerModel | undefined {
|
|
32
|
+
if (entitlements.state === 'trialClosed') {
|
|
33
|
+
return { severity: 'error', text: labels.trialBanner.closed };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (entitlements.state !== 'trial' || !entitlements.trial) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const { daysLeft } = entitlements.trial;
|
|
41
|
+
if (daysLeft <= 0) {
|
|
42
|
+
return { severity: 'warn', text: labels.trialBanner.lastDay };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
severity: daysLeft <= trialWarningDays ? 'warn' : 'info',
|
|
47
|
+
text: format(labels.trialBanner.running, { days: daysLeft })
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Says why the operator suspended a tenant.
|
|
53
|
+
* @param cause The cause the operator gave.
|
|
54
|
+
* @param labels The labels to show.
|
|
55
|
+
* @returns The reason, in words.
|
|
56
|
+
*/
|
|
57
|
+
export function suspensionReason(cause: SuspensionCause | undefined, labels: BoxOfficeLabels): string {
|
|
58
|
+
switch (cause) {
|
|
59
|
+
case SuspensionCause.trialEndedWithoutSeats:
|
|
60
|
+
return labels.suspension.trialEndedWithoutSeats;
|
|
61
|
+
case SuspensionCause.paymentOverdue:
|
|
62
|
+
return labels.suspension.paymentOverdue;
|
|
63
|
+
case SuspensionCause.administratorDecision:
|
|
64
|
+
return labels.suspension.administratorDecision;
|
|
65
|
+
default:
|
|
66
|
+
return '';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Why a user is told they have no license. */
|
|
71
|
+
export type NoLicenseReason = 'suspended' | 'trialClosed' | 'noSeat' | 'overCapacity';
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* What a user without a license is told.
|
|
75
|
+
*/
|
|
76
|
+
export interface NoLicenseModel {
|
|
77
|
+
/** Why the user has no license. */
|
|
78
|
+
readonly reason: NoLicenseReason;
|
|
79
|
+
|
|
80
|
+
/** The title. */
|
|
81
|
+
readonly title: string;
|
|
82
|
+
|
|
83
|
+
/** What to do about it. */
|
|
84
|
+
readonly text: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Works out whether the user has a license, and what to tell them if not. The tenant being read-only comes first,
|
|
89
|
+
* because it holds for everyone in it.
|
|
90
|
+
* @param entitlements What the tenant may do.
|
|
91
|
+
* @param seat Where the user stands with the tenant's seats; nothing while it loads.
|
|
92
|
+
* @param labels The labels to show.
|
|
93
|
+
* @returns What to tell the user; undefined when they have a license, or it is not known yet.
|
|
94
|
+
*/
|
|
95
|
+
export function noLicenseModel(entitlements: Entitlements, seat: SeatStanding | undefined, labels: BoxOfficeLabels): NoLicenseModel | undefined {
|
|
96
|
+
if (entitlements.isLoading) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (entitlements.readOnlyReason === ReadOnlyReason.tenantSuspended) {
|
|
101
|
+
return { reason: 'suspended', title: labels.noLicense.suspendedTitle, text: labels.noLicense.suspended };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (entitlements.readOnlyReason === ReadOnlyReason.trialClosed) {
|
|
105
|
+
return { reason: 'trialClosed', title: labels.noLicense.trialClosedTitle, text: labels.noLicense.trialClosed };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (seat?.status === SeatStatus.noSeat) {
|
|
109
|
+
return { reason: 'noSeat', title: labels.noLicense.noSeatTitle, text: labels.noLicense.noSeat };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (seat?.status === SeatStatus.overCapacity) {
|
|
113
|
+
return { reason: 'overCapacity', title: labels.noLicense.overCapacityTitle, text: labels.noLicense.overCapacity };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
package/Banners/index.ts
ADDED
|
@@ -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 './bannerModels.js';
|
|
5
|
+
export * from './NoLicense.js';
|
|
6
|
+
export * from './SuspensionBanner.js';
|
|
7
|
+
export * from './TrialBanner.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
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 { createContext, useContext } from 'react';
|
|
5
|
+
import { Entitlements, entitlementsOf } from './entitlementModel.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The React context {@link BoxOfficeProvider} provides the entitlements through. Without a provider nothing is
|
|
9
|
+
* known, so nothing that belongs to a module is shown.
|
|
10
|
+
*/
|
|
11
|
+
export const EntitlementsContext = createContext<Entitlements>(entitlementsOf(undefined));
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Gets what the tenant may do.
|
|
15
|
+
* @returns The {@link Entitlements}.
|
|
16
|
+
*/
|
|
17
|
+
export const useEntitlementsContext = (): Entitlements => useContext(EntitlementsContext);
|
|
@@ -0,0 +1,68 @@
|
|
|
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 } from '@cratis/components/Display';
|
|
5
|
+
import { PortalIntent } from '@cratis/showrunner.boxoffice';
|
|
6
|
+
import { ReactNode } from 'react';
|
|
7
|
+
import { PortalButton } from '../Portal/PortalButton.js';
|
|
8
|
+
import { useBoxOfficeLabels } from '../Provider/BoxOfficeContext.js';
|
|
9
|
+
import { useEntitlementsContext } from './EntitlementsContext.js';
|
|
10
|
+
import { requiresModuleModel } from './requiresModuleModel.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Props for {@link RequiresModule}.
|
|
14
|
+
*/
|
|
15
|
+
export interface RequiresModuleProps {
|
|
16
|
+
/** The module whose content this is. */
|
|
17
|
+
module: string;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* What to do when the tenant does not have the module: `hide` shows the fallback, which is nothing unless given;
|
|
21
|
+
* `explain` says why it is not there, and offers to try or buy it. Defaults to `hide`.
|
|
22
|
+
*/
|
|
23
|
+
mode?: 'hide' | 'explain';
|
|
24
|
+
|
|
25
|
+
/** What to show instead of the content in `hide` mode. Nothing when not given. */
|
|
26
|
+
fallback?: ReactNode;
|
|
27
|
+
|
|
28
|
+
/** The content of the module. */
|
|
29
|
+
children?: ReactNode;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Shows its content only when the tenant has the module, in full or on trial. Nothing is shown while the entitlements
|
|
34
|
+
* load, so the content never flashes up and is taken away. The backend refuses the module's commands and queries on its
|
|
35
|
+
* own; this is what keeps the user from meeting the refusal.
|
|
36
|
+
*/
|
|
37
|
+
export const RequiresModule = ({ module, mode = 'hide', fallback, children }: RequiresModuleProps) => {
|
|
38
|
+
const entitlements = useEntitlementsContext();
|
|
39
|
+
const labels = useBoxOfficeLabels();
|
|
40
|
+
|
|
41
|
+
if (entitlements.isLoading) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const model = requiresModuleModel(entitlements, module, labels);
|
|
46
|
+
if (model.hasModule) {
|
|
47
|
+
return <>{children}</>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (mode === 'hide') {
|
|
51
|
+
return <>{fallback}</>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div className='boxoffice-requires-module'>
|
|
56
|
+
<Message severity='info'>
|
|
57
|
+
<div className='boxoffice-requires-module__text'>
|
|
58
|
+
<strong>{model.title}</strong>
|
|
59
|
+
<span>{model.text}</span>
|
|
60
|
+
</div>
|
|
61
|
+
</Message>
|
|
62
|
+
<div className='boxoffice-requires-module__actions'>
|
|
63
|
+
{model.canTry && <PortalButton intent={PortalIntent.try} label={labels.requiresModule.try} variant='outline' />}
|
|
64
|
+
<PortalButton intent={PortalIntent.buy} label={labels.requiresModule.buy} />
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
@@ -0,0 +1,175 @@
|
|
|
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 { DateOnly } from '@cratis/fundamentals';
|
|
5
|
+
import { EntitlementSummary, ModuleAccess, ModuleStanding, ReadOnlyReason } from '@cratis/showrunner.boxoffice';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Where a tenant stands with its license, as one word.
|
|
9
|
+
*
|
|
10
|
+
* - `loading`: nothing is known yet.
|
|
11
|
+
* - `suspended`: the operator suspended the tenant; it is read-only.
|
|
12
|
+
* - `trialClosed`: the trial ended without seats being bought; the tenant is read-only.
|
|
13
|
+
* - `trial`: the tenant is on a trial that runs.
|
|
14
|
+
* - `licensed`: the tenant has bought seats.
|
|
15
|
+
* - `unlicensed`: the tenant has no seats and is not on a trial.
|
|
16
|
+
*/
|
|
17
|
+
export type LicenseState = 'loading' | 'suspended' | 'trialClosed' | 'trial' | 'licensed' | 'unlicensed';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A trial that is running or has closed.
|
|
21
|
+
*/
|
|
22
|
+
export interface TrialStanding {
|
|
23
|
+
/** The last day of the trial. */
|
|
24
|
+
readonly ends: Date;
|
|
25
|
+
|
|
26
|
+
/** How many days are left; zero on the last day, and less than zero after it. */
|
|
27
|
+
readonly daysLeft: number;
|
|
28
|
+
|
|
29
|
+
/** Whether the trial has closed. */
|
|
30
|
+
readonly isClosed: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* What a tenant may do, for a component to decide what to show.
|
|
35
|
+
*/
|
|
36
|
+
export interface Entitlements {
|
|
37
|
+
/** Whether nothing is known about the tenant yet. */
|
|
38
|
+
readonly isLoading: boolean;
|
|
39
|
+
|
|
40
|
+
/** Where the tenant stands with its license. */
|
|
41
|
+
readonly state: LicenseState;
|
|
42
|
+
|
|
43
|
+
/** Whether the tenant can read but not change anything. */
|
|
44
|
+
readonly isReadOnly: boolean;
|
|
45
|
+
|
|
46
|
+
/** Why the tenant is read-only; none when it is not. */
|
|
47
|
+
readonly readOnlyReason: ReadOnlyReason;
|
|
48
|
+
|
|
49
|
+
/** How many seats the tenant has today. */
|
|
50
|
+
readonly seats: number;
|
|
51
|
+
|
|
52
|
+
/** The trial, when the tenant is on one or was. */
|
|
53
|
+
readonly trial?: TrialStanding;
|
|
54
|
+
|
|
55
|
+
/** Whether the product has a customer portal to buy, try and change plan in. */
|
|
56
|
+
readonly canOpenPortal: boolean;
|
|
57
|
+
|
|
58
|
+
/** Every module the product declares, and what the tenant may do with it. */
|
|
59
|
+
readonly modules: readonly ModuleStanding[];
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Gets whether the tenant has a module: in full or on trial. A preview does not count.
|
|
63
|
+
* @param module The module.
|
|
64
|
+
* @returns True when the tenant has the module.
|
|
65
|
+
*/
|
|
66
|
+
has(module: string): boolean;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Gets the access the tenant has to a module.
|
|
70
|
+
* @param module The module.
|
|
71
|
+
* @returns The access; none for a module the product does not declare.
|
|
72
|
+
*/
|
|
73
|
+
accessTo(module: string): ModuleAccess;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Gets what the tenant may do with a module.
|
|
77
|
+
* @param module The module.
|
|
78
|
+
* @returns The standing, or undefined for a module the product does not declare.
|
|
79
|
+
*/
|
|
80
|
+
standingOf(module: string): ModuleStanding | undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const millisecondsInADay = 24 * 60 * 60 * 1000;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Turns a day from a proxy into a date.
|
|
87
|
+
* @param day The day: a `DateOnly`, a `yyyy-mm-dd` text, or a date.
|
|
88
|
+
* @returns The date at midnight local time; undefined when there is no day.
|
|
89
|
+
*/
|
|
90
|
+
export function toDate(day: DateOnly | Date | string | undefined): Date | undefined {
|
|
91
|
+
if (day === undefined || day === null) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (day instanceof Date) {
|
|
96
|
+
return new Date(day.getFullYear(), day.getMonth(), day.getDate());
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const dateOnly = typeof day === 'string' ? DateOnly.parse(day.substring(0, 10)) : day;
|
|
100
|
+
return dateOnly.toDate();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Counts the days from one day to another, ignoring the time of day.
|
|
105
|
+
* @param from The day to count from.
|
|
106
|
+
* @param to The day to count to.
|
|
107
|
+
* @returns How many days `to` is after `from`; less than zero when it is before.
|
|
108
|
+
*/
|
|
109
|
+
export function daysBetween(from: Date, to: Date): number {
|
|
110
|
+
const start = Date.UTC(from.getFullYear(), from.getMonth(), from.getDate());
|
|
111
|
+
const end = Date.UTC(to.getFullYear(), to.getMonth(), to.getDate());
|
|
112
|
+
return Math.round((end - start) / millisecondsInADay);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function stateOf(summary: EntitlementSummary, trial: TrialStanding | undefined): LicenseState {
|
|
116
|
+
if (summary.readOnlyReason === ReadOnlyReason.tenantSuspended) {
|
|
117
|
+
return 'suspended';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (summary.readOnlyReason === ReadOnlyReason.trialClosed) {
|
|
121
|
+
return 'trialClosed';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (summary.isTrial && trial && !trial.isClosed) {
|
|
125
|
+
return 'trial';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return summary.seats > 0 ? 'licensed' : 'unlicensed';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Works out what a tenant may do from what the backend says.
|
|
133
|
+
* @param summary The entitlement summary; nothing while it loads.
|
|
134
|
+
* @param today The day it is. Defaults to today.
|
|
135
|
+
* @returns The {@link Entitlements}.
|
|
136
|
+
*/
|
|
137
|
+
export function entitlementsOf(summary: EntitlementSummary | undefined, today: Date = new Date()): Entitlements {
|
|
138
|
+
if (!summary) {
|
|
139
|
+
return {
|
|
140
|
+
isLoading: true,
|
|
141
|
+
state: 'loading',
|
|
142
|
+
isReadOnly: false,
|
|
143
|
+
readOnlyReason: ReadOnlyReason.none,
|
|
144
|
+
seats: 0,
|
|
145
|
+
canOpenPortal: false,
|
|
146
|
+
modules: [],
|
|
147
|
+
has: () => false,
|
|
148
|
+
accessTo: () => ModuleAccess.none,
|
|
149
|
+
standingOf: () => undefined
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const modules = summary.modules ?? [];
|
|
154
|
+
const ends = summary.isTrial ? toDate(summary.trialEnds) : undefined;
|
|
155
|
+
const daysLeft = ends ? daysBetween(today, ends) : 0;
|
|
156
|
+
const trial = ends
|
|
157
|
+
? { ends, daysLeft, isClosed: daysLeft < 0 || summary.readOnlyReason === ReadOnlyReason.trialClosed }
|
|
158
|
+
: undefined;
|
|
159
|
+
const standingOf = (module: string) => modules.find(_ => _.module === module);
|
|
160
|
+
const accessTo = (module: string) => standingOf(module)?.access ?? ModuleAccess.none;
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
isLoading: false,
|
|
164
|
+
state: stateOf(summary, trial),
|
|
165
|
+
isReadOnly: summary.readOnlyReason !== ReadOnlyReason.none,
|
|
166
|
+
readOnlyReason: summary.readOnlyReason,
|
|
167
|
+
seats: summary.seats,
|
|
168
|
+
trial,
|
|
169
|
+
canOpenPortal: summary.canOpenPortal,
|
|
170
|
+
modules,
|
|
171
|
+
has: module => accessTo(module) === ModuleAccess.trial || accessTo(module) === ModuleAccess.full,
|
|
172
|
+
accessTo,
|
|
173
|
+
standingOf
|
|
174
|
+
};
|
|
175
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
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 './entitlementModel.js';
|
|
5
|
+
export * from './EntitlementsContext.js';
|
|
6
|
+
export * from './moduleFilter.js';
|
|
7
|
+
export * from './requiresModuleModel.js';
|
|
8
|
+
export * from './RequiresModule.js';
|
|
9
|
+
export * from './useEntitlements.js';
|
|
@@ -0,0 +1,23 @@
|
|
|
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 { Entitlements } from './entitlementModel.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Something that belongs to a module: a navigation entry, a page, a menu item or a route.
|
|
8
|
+
*/
|
|
9
|
+
export interface ModuleOwned {
|
|
10
|
+
/** The module that owns it; leave it out for what belongs to the product itself and is always shown. */
|
|
11
|
+
module?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Leaves out what belongs to a module the tenant does not have, so navigation and pages disappear without it. While
|
|
16
|
+
* the entitlements load, what belongs to a module is left out too, rather than shown and taken away.
|
|
17
|
+
* @param items The items, such as the entries of a sidebar.
|
|
18
|
+
* @param entitlements What the tenant may do.
|
|
19
|
+
* @returns The items the tenant may see.
|
|
20
|
+
*/
|
|
21
|
+
export function filterByModule<TItem extends ModuleOwned>(items: readonly TItem[], entitlements: Entitlements): TItem[] {
|
|
22
|
+
return items.filter(item => item.module === undefined || entitlements.has(item.module));
|
|
23
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
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 { ModuleAccess, ModuleStanding } from '@cratis/showrunner.boxoffice';
|
|
5
|
+
import { BoxOfficeLabels, format } from '../Labels/index.js';
|
|
6
|
+
import { Entitlements } from './entitlementModel.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* What to tell the user about a module they do not have.
|
|
10
|
+
*/
|
|
11
|
+
export interface RequiresModuleModel {
|
|
12
|
+
/** Whether the tenant has the module, so the content shows. */
|
|
13
|
+
readonly hasModule: boolean;
|
|
14
|
+
|
|
15
|
+
/** The title of the explanation. */
|
|
16
|
+
readonly title: string;
|
|
17
|
+
|
|
18
|
+
/** The explanation. */
|
|
19
|
+
readonly text: string;
|
|
20
|
+
|
|
21
|
+
/** Whether to offer to try the module. */
|
|
22
|
+
readonly canTry: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Works out what to show where a module's content would be.
|
|
27
|
+
* @param entitlements What the tenant may do.
|
|
28
|
+
* @param module The module.
|
|
29
|
+
* @param labels The labels to show.
|
|
30
|
+
* @returns The {@link RequiresModuleModel}.
|
|
31
|
+
*/
|
|
32
|
+
export function requiresModuleModel(entitlements: Entitlements, module: string, labels: BoxOfficeLabels): RequiresModuleModel {
|
|
33
|
+
const standing: ModuleStanding | undefined = entitlements.standingOf(module);
|
|
34
|
+
const name = standing?.name ?? module;
|
|
35
|
+
const access = standing?.access ?? ModuleAccess.none;
|
|
36
|
+
const text = access === ModuleAccess.preview ? labels.requiresModule.preview : labels.requiresModule.none;
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
hasModule: entitlements.has(module),
|
|
40
|
+
title: format(labels.requiresModule.title, { module: name }),
|
|
41
|
+
text: format(text, { module: name }),
|
|
42
|
+
canTry: access === ModuleAccess.none || access === ModuleAccess.preview
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
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 { Entitlements } from './entitlementModel.js';
|
|
5
|
+
import { useEntitlementsContext } from './EntitlementsContext.js';
|
|
6
|
+
import { filterByModule, ModuleOwned } from './moduleFilter.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Gets what the signed-in user's tenant may do: whether it is on a trial, read-only or suspended, and which modules it
|
|
10
|
+
* has. Use it inside a {@link BoxOfficeProvider}.
|
|
11
|
+
* @returns The {@link Entitlements}.
|
|
12
|
+
*/
|
|
13
|
+
export function useEntitlements(): Entitlements {
|
|
14
|
+
return useEntitlementsContext();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Leaves out what belongs to a module the tenant does not have, for the entries of a shell's navigation. Use it
|
|
19
|
+
* inside a {@link BoxOfficeProvider}.
|
|
20
|
+
* @param items The items, each optionally owned by a module.
|
|
21
|
+
* @returns The items the tenant may see.
|
|
22
|
+
*/
|
|
23
|
+
export function useModuleFilter<TItem extends ModuleOwned>(items: readonly TItem[]): TItem[] {
|
|
24
|
+
return filterByModule(items, useEntitlementsContext());
|
|
25
|
+
}
|