diff --git a/jslib b/jslib index 141ade3c38..4550b7ddf8 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit 141ade3c381cb9ec8e89f15061b92330cb32d403 +Subproject commit 4550b7ddf819b4f4f0a5d62376df6f7bf2425118 diff --git a/src/app/modules/loose-components.module.ts b/src/app/modules/loose-components.module.ts index dc8bb6a383..34504c6101 100644 --- a/src/app/modules/loose-components.module.ts +++ b/src/app/modules/loose-components.module.ts @@ -61,6 +61,7 @@ import { AdjustSubscription } from "../organizations/settings/adjust-subscriptio import { ChangePlanComponent } from "../organizations/settings/change-plan.component"; import { DeleteOrganizationComponent } from "../organizations/settings/delete-organization.component"; import { DownloadLicenseComponent } from "../organizations/settings/download-license.component"; +import { ImageSubscriptionHiddenComponent as OrgSubscriptionHiddenComponent } from "../organizations/settings/image-subscription-hidden.component"; import { OrganizationBillingComponent } from "../organizations/settings/organization-billing.component"; import { OrganizationSubscriptionComponent } from "../organizations/settings/organization-subscription.component"; import { SettingsComponent as OrgSettingComponent } from "../organizations/settings/settings.component"; @@ -256,6 +257,7 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga OrgSettingComponent, OrgToolsComponent, OrgTwoFactorSetupComponent, + OrgSubscriptionHiddenComponent, OrgUnsecuredWebsitesReportComponent, OrgUserAddEditComponent, OrgUserConfirmComponent, diff --git a/src/app/organizations/organization-routing.module.ts b/src/app/organizations/organization-routing.module.ts index ba92c1e39d..cae00bec56 100644 --- a/src/app/organizations/organization-routing.module.ts +++ b/src/app/organizations/organization-routing.module.ts @@ -201,7 +201,8 @@ const routes: Routes = [ { path: "billing", component: OrganizationBillingComponent, - data: { titleId: "billing" }, + canActivate: [PermissionsGuard], + data: { titleId: "billing", permissions: [Permissions.ManageBilling] }, }, { path: "subscription", diff --git a/src/app/organizations/settings/account.component.html b/src/app/organizations/settings/account.component.html index 8995d8aa0a..7f237203d8 100644 --- a/src/app/organizations/settings/account.component.html +++ b/src/app/organizations/settings/account.component.html @@ -37,7 +37,7 @@ type="text" name="BillingEmail" [(ngModel)]="org.billingEmail" - [disabled]="selfHosted" + [disabled]="selfHosted || !canManageBilling" />
@@ -48,7 +48,7 @@ type="text" name="BusinessName" [(ngModel)]="org.businessName" - [disabled]="selfHosted" + [disabled]="selfHosted || !canManageBilling" />
diff --git a/src/app/organizations/settings/account.component.ts b/src/app/organizations/settings/account.component.ts index ec6b225984..291c8f9e49 100644 --- a/src/app/organizations/settings/account.component.ts +++ b/src/app/organizations/settings/account.component.ts @@ -6,6 +6,7 @@ import { ApiService } from "jslib-common/abstractions/api.service"; import { CryptoService } from "jslib-common/abstractions/crypto.service"; import { I18nService } from "jslib-common/abstractions/i18n.service"; import { LogService } from "jslib-common/abstractions/log.service"; +import { OrganizationService } from "jslib-common/abstractions/organization.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { SyncService } from "jslib-common/abstractions/sync.service"; import { OrganizationKeysRequest } from "jslib-common/models/request/organizationKeysRequest"; @@ -34,6 +35,7 @@ export class AccountComponent { @ViewChild(TaxInfoComponent) taxInfo: TaxInfoComponent; selfHosted = false; + canManageBilling = true; loading = true; canUseApi = false; org: OrganizationResponse; @@ -51,13 +53,18 @@ export class AccountComponent { private platformUtilsService: PlatformUtilsService, private cryptoService: CryptoService, private logService: LogService, - private router: Router + private router: Router, + private organizationService: OrganizationService ) {} async ngOnInit() { this.selfHosted = this.platformUtilsService.isSelfHost(); + this.route.parent.parent.params.subscribe(async (params) => { this.organizationId = params.organizationId; + this.canManageBilling = ( + await this.organizationService.get(this.organizationId) + ).canManageBilling; try { this.org = await this.apiService.getOrganization(this.organizationId); this.canUseApi = this.org.useApi; diff --git a/src/app/organizations/settings/image-subscription-hidden.component.svg b/src/app/organizations/settings/image-subscription-hidden.component.svg new file mode 100644 index 0000000000..a45a3decce --- /dev/null +++ b/src/app/organizations/settings/image-subscription-hidden.component.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/organizations/settings/image-subscription-hidden.component.ts b/src/app/organizations/settings/image-subscription-hidden.component.ts new file mode 100644 index 0000000000..ff2a639e39 --- /dev/null +++ b/src/app/organizations/settings/image-subscription-hidden.component.ts @@ -0,0 +1,8 @@ +import { Component } from "@angular/core"; + +// Component is used so that the SVG can embed CSS color variables +@Component({ + selector: "app-image-org-subscription-hidden", + templateUrl: "image-subscription-hidden.component.svg", +}) +export class ImageSubscriptionHiddenComponent {} diff --git a/src/app/organizations/settings/organization-subscription.component.html b/src/app/organizations/settings/organization-subscription.component.html index 239935fb74..20e93ceb8f 100644 --- a/src/app/organizations/settings/organization-subscription.component.html +++ b/src/app/organizations/settings/organization-subscription.component.html @@ -16,6 +16,14 @@ {{ "loading" | i18n }} + +
+ +

{{ "billingManagedByProvider" | i18n: this.userOrg.providerName }}

+

{{ "billingContactProviderForAssistance" | i18n }}

+
+
+ {{ "billing" | i18n }} diff --git a/src/app/organizations/settings/settings.component.ts b/src/app/organizations/settings/settings.component.ts index 9f4473304b..d75790944d 100644 --- a/src/app/organizations/settings/settings.component.ts +++ b/src/app/organizations/settings/settings.component.ts @@ -10,7 +10,7 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se }) export class SettingsComponent { access2fa = false; - selfHosted: boolean; + showBilling: boolean; constructor( private route: ActivatedRoute, @@ -20,8 +20,8 @@ export class SettingsComponent { ngOnInit() { this.route.parent.params.subscribe(async (params) => { - this.selfHosted = await this.platformUtilsService.isSelfHost(); const organization = await this.organizationService.get(params.organizationId); + this.showBilling = !this.platformUtilsService.isSelfHost() && organization.canManageBilling; this.access2fa = organization.use2fa; }); } diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index 95d6715606..2cd1ea02ed 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -4935,6 +4935,19 @@ "service": { "message": "Service" }, + "billingManagedByProvider": { + "message": "Managed by $PROVIDER$", + "placeholders": { + "provider": { + "content": "$1", + "example": "Managed Services Company" + } + } + }, + "billingContactProviderForAssistance": { + "message": "Please reach out to them for further assistance", + "description": "This text is displayed if an organization's billing is managed by a Provider. It tells the user to contact the Provider for assistance." + }, "forwardedEmail": { "message": "Forwarded Email Alias" },