(core) Show usage banners in doc menu of free team sites

Summary:
Also fixes a minor CSS regression in UserManager where the
link to add a team member wasn't shown on a separate row.

Test Plan: Browser tests.

Reviewers: jarek

Reviewed By: jarek

Differential Revision: https://phab.getgrist.com/D3444
This commit is contained in:
George Gevoian
2022-05-25 23:47:26 -07:00
parent 2f3cf59fc3
commit 74ec9358da
16 changed files with 341 additions and 213 deletions

View File

@@ -3,6 +3,12 @@ export interface SnapshotWindow {
unit: 'days' | 'month' | 'year';
}
// Information about the product associated with an org or orgs.
export interface Product {
name: string;
features: Features;
}
// A product is essentially a list of flags and limits that we may enforce/support.
export interface Features {
vanityDomain?: boolean; // are user-selected domains allowed (unenforced) (default: true)
@@ -60,3 +66,8 @@ export interface Features {
export function canAddOrgMembers(features: Features): boolean {
return features.maxWorkspacesPerOrg !== 1;
}
// Returns true if `product` is free.
export function isFreeProduct(product: Product): boolean {
return ['starter', 'teamFree'].includes(product.name);
}

View File

@@ -6,7 +6,7 @@ import {BrowserSettings} from 'app/common/BrowserSettings';
import {BulkColValues, TableColValues, TableRecordValue, TableRecordValues, UserAction} from 'app/common/DocActions';
import {DocCreationInfo, OpenDocMode} from 'app/common/DocListAPI';
import {OrgUsageSummary} from 'app/common/DocUsage';
import {Features} from 'app/common/Features';
import {Product} from 'app/common/Features';
import {ICustomWidget} from 'app/common/CustomWidget';
import {isClient} from 'app/common/gristUrls';
import {FullUser} from 'app/common/LoginSessionAPI';
@@ -73,12 +73,6 @@ export interface BillingAccount {
};
}
// Information about the product associated with an org or orgs.
export interface Product {
name: string;
features: Features;
}
// The upload types vary based on which fetch implementation is in use. This is
// an incomplete list. For example, node streaming types are supported by node-fetch.
export type UploadType = string | Blob | Buffer;

View File

@@ -1,3 +1,4 @@
import {isOwner} from 'app/common/roles';
import {ManagerDelta, PermissionDelta, UserAPI} from 'app/common/UserAPI';
/**
@@ -7,7 +8,7 @@ import {ManagerDelta, PermissionDelta, UserAPI} from 'app/common/UserAPI';
*/
export async function resetOrg(api: UserAPI, org: string|number) {
const session = await api.getSessionActive();
if (!(session.org && session.org.access === 'owners')) {
if (!isOwner(session.org)) {
throw new Error('user must be an owner of the org to be reset');
}
const billing = api.getBillingAPI();

View File

@@ -1,3 +1,5 @@
import {Organization} from 'app/common/UserAPI';
export const OWNER = 'owners';
export const EDITOR = 'editors';
export const VIEWER = 'viewers';
@@ -39,6 +41,15 @@ export function canView(role: string|null): boolean {
return role !== null;
}
export function isOwner(resource: {access: Role}|null): resource is {access: Role} {
return resource?.access === OWNER;
}
export function canUpgradeOrg(org: Organization|null): org is Organization {
// TODO: Need to consider billing managers and support user.
return isOwner(org);
}
// Returns true if the role string is a valid role or null.
export function isValidRole(role: string|null): role is Role|null {
return (roleOrder as Array<string|null>).includes(role);