From aeba738f7c675c3be0e6f9cb006bb548fe752f95 Mon Sep 17 00:00:00 2001 From: George Gevoian Date: Tue, 26 Jul 2022 10:49:35 -0700 Subject: [PATCH] (core) Add product for new personal plan Summary: Adds the new personal plan as a product that will be available in the future. Can be enabled along with other plan-related via an environment variable. Test Plan: Browser tests and existing tests. Reviewers: jarek Reviewed By: jarek Subscribers: dsagal Differential Revision: https://phab.getgrist.com/D3533 --- app/client/components/Banner.ts | 2 +- app/client/components/DocumentUsage.ts | 33 ++++++++++------- app/client/models/AppModel.ts | 19 ++++++---- app/client/models/UserManagerModel.ts | 4 +- app/client/ui/AppHeader.ts | 6 ++- app/client/ui/BillingPage.ts | 5 +-- app/client/ui/DocMenu.ts | 21 +++++++---- app/client/ui/HomeLeftPane.ts | 8 +++- app/client/ui/NotifyUI.ts | 10 +++-- app/client/ui2018/menus.ts | 10 +++-- app/common/Features.ts | 47 ++++++++++++++++++++++-- app/common/ShareAnnotator.ts | 8 ++-- app/common/gristUrls.ts | 3 ++ app/gen-server/entity/Product.ts | 51 +++++++++++++++++++------- app/gen-server/lib/HomeDBManager.ts | 8 ++-- app/server/lib/sendAppPage.ts | 1 + test/gen-server/seed.ts | 27 +++++++++++++- test/nbrowser/gristUtils.ts | 4 -- 18 files changed, 194 insertions(+), 73 deletions(-) diff --git a/app/client/components/Banner.ts b/app/client/components/Banner.ts index e4ae9563..0f526fb7 100644 --- a/app/client/components/Banner.ts +++ b/app/client/components/Banner.ts @@ -139,7 +139,7 @@ const cssButton = styled(icon, ` const cssExpandButton = styled(cssButton, ` &-expanded { - -webkit-mask-image: var(--icon-DropdownUp); + -webkit-mask-image: var(--icon-DropdownUp) !important; } `); diff --git a/app/client/components/DocumentUsage.ts b/app/client/components/DocumentUsage.ts index 16537c9b..f7aa7974 100644 --- a/app/client/components/DocumentUsage.ts +++ b/app/client/components/DocumentUsage.ts @@ -4,11 +4,9 @@ import {docListHeader} from 'app/client/ui/DocMenuCss'; import {infoTooltip} from 'app/client/ui/tooltips'; import {colors, mediaXSmall} from 'app/client/ui2018/cssVars'; import {icon} from 'app/client/ui2018/icons'; -import {cssLink} from 'app/client/ui2018/links'; import {loadingSpinner} from 'app/client/ui2018/loaders'; import {APPROACHING_LIMIT_RATIO, DataLimitStatus} from 'app/common/DocUsage'; -import {Features, isFreeProduct} from 'app/common/Features'; -import {commonUrls} from 'app/common/gristUrls'; +import {Features, isFreePlan} from 'app/common/Features'; import {capitalizeFirstWord} from 'app/common/gutil'; import {canUpgradeOrg} from 'app/common/roles'; import {Computed, Disposable, dom, DomContents, DomElementArg, makeTestId, styled} from 'grainjs'; @@ -168,8 +166,12 @@ export class DocumentUsage extends Disposable { buildLimitStatusMessage(status, product?.features, { disableRawDataLink: true }), - (product && isFreeProduct(product) - ? [' ', buildUpgradeMessage(canUpgradeOrg(org))] + (product && isFreePlan(product.name) + ? [' ', buildUpgradeMessage( + canUpgradeOrg(org), + 'long', + () => this._docPageModel.appModel.showUpgradeModal() + )] : null ), ]); @@ -236,21 +238,25 @@ export function buildLimitStatusMessage( } } -export function buildUpgradeMessage(canUpgrade: boolean, variant: 'short' | 'long' = 'long') { +export function buildUpgradeMessage( + canUpgrade: boolean, + variant: 'short' | 'long', + onUpgrade: () => void, +) { if (!canUpgrade) { return 'Contact the site owner to upgrade the plan to raise limits.'; } const upgradeLinkText = 'start your 30-day free trial of the Pro plan.'; return [ variant === 'short' ? null : 'For higher limits, ', - buildUpgradeLink(variant === 'short' ? capitalizeFirstWord(upgradeLinkText) : upgradeLinkText), + buildUpgradeLink( + variant === 'short' ? capitalizeFirstWord(upgradeLinkText) : upgradeLinkText, + () => onUpgrade(), + ), ]; } -function buildUpgradeLink(linkText: string) { - return cssUnderlinedLink(linkText, { - href: commonUrls.plans, - target: '_blank', - }); +function buildUpgradeLink(linkText: string, onClick: () => void) { + return cssUnderlinedLink(linkText, dom.on('click', () => onClick())); } function buildRawDataPageLink(linkText: string) { @@ -356,7 +362,8 @@ const cssHeader = styled(docListHeader, ` margin-bottom: 0px; `); -const cssUnderlinedLink = styled(cssLink, ` +const cssUnderlinedLink = styled('span', ` + cursor: pointer; color: unset; text-decoration: underline; diff --git a/app/client/models/AppModel.ts b/app/client/models/AppModel.ts index 47b0ca47..dfe58f69 100644 --- a/app/client/models/AppModel.ts +++ b/app/client/models/AppModel.ts @@ -4,8 +4,9 @@ import {reportError, setErrorNotifier} from 'app/client/models/errors'; import {urlState} from 'app/client/models/gristUrlState'; import {Notifier} from 'app/client/models/NotifyModel'; import {getFlavor, ProductFlavor} from 'app/client/ui/CustomThemes'; +import {buildNewSiteModal, buildUpgradeModal, NEW_DEAL} from 'app/client/ui/ProductUpgrades'; import {OrgUsageSummary} from 'app/common/DocUsage'; -import {Features} from 'app/common/Features'; +import {Features, isLegacyPlan, Product} from 'app/common/Features'; import {GristLoadConfig} from 'app/common/gristUrls'; import {FullUser} from 'app/common/LoginSessionAPI'; import {LocalPlugin} from 'app/common/plugin'; @@ -16,7 +17,6 @@ import {getGristConfig} from 'app/common/urlUtils'; import {getOrgName, Organization, OrgError, SUPPORT_EMAIL, UserAPI, UserAPIImpl} from 'app/common/UserAPI'; import {getUserPrefObs, getUserPrefsObs} from 'app/client/models/UserPrefs'; import {bundleChanges, Computed, Disposable, Observable, subscribe} from 'grainjs'; -import {buildNewSiteModal, buildUpgradeModal} from 'app/client/ui/ProductUpgrades'; export {reportError} from 'app/client/models/errors'; @@ -70,9 +70,11 @@ export interface AppModel { currentOrgUsage: Observable; isPersonal: boolean; // Is it a personal site? isTeamSite: boolean; // Is it a team site? + isLegacySite: boolean; // Is it a legacy site? orgError?: OrgError; // If currentOrg is null, the error that caused it. - currentFeatures: Features; // features of the current org's product. + currentProduct: Product|null; // The current org's product. + currentFeatures: Features; // Features of the current org's product. userPrefsObs: Observable; pageType: Observable; @@ -199,11 +201,14 @@ export class AppModelImpl extends Disposable implements AppModel { public readonly currentOrgUsage: Observable = Observable.create(this, null); + public readonly currentProduct = this.currentOrg?.billingAccount?.product ?? null; + public readonly currentFeatures = this.currentProduct?.features ?? {}; + public readonly isPersonal = Boolean(this.currentOrg?.owner); public readonly isTeamSite = Boolean(this.currentOrg) && !this.isPersonal; - - public readonly currentFeatures = (this.currentOrg && this.currentOrg.billingAccount) ? - this.currentOrg.billingAccount.product.features : {}; + // TODO: the `NEW_DEAL` observable can be removed after new deal is released. + public readonly isLegacySite = Boolean( + NEW_DEAL().get() && this.currentProduct && isLegacyPlan(this.currentProduct.name)); public readonly userPrefsObs = getUserPrefsObs(this); @@ -224,7 +229,7 @@ export class AppModelImpl extends Disposable implements AppModel { } public get planName() { - return this.currentOrg?.billingAccount?.product.name ?? null; + return this.currentProduct?.name ?? null; } public async showUpgradeModal() { diff --git a/app/client/models/UserManagerModel.ts b/app/client/models/UserManagerModel.ts index fe356fda..22362869 100644 --- a/app/client/models/UserManagerModel.ts +++ b/app/client/models/UserManagerModel.ts @@ -171,8 +171,8 @@ export class UserManagerModelImpl extends Disposable implements UserManagerModel ) { super(); if (this._options.appModel) { - const features = this._options.appModel.currentFeatures; - this._shareAnnotator = new ShareAnnotator(features, initData); + const product = this._options.appModel.currentProduct; + this._shareAnnotator = new ShareAnnotator(product, initData); } this.annotate(); } diff --git a/app/client/ui/AppHeader.ts b/app/client/ui/AppHeader.ts index 8c659d6d..43d3357e 100644 --- a/app/client/ui/AppHeader.ts +++ b/app/client/ui/AppHeader.ts @@ -51,7 +51,11 @@ export class AppHeader extends Disposable { productPill(currentOrg), this._orgName && cssDropdownIcon('Dropdown'), menu(() => [ - menuSubHeader(`${this._appModel.isTeamSite ? 'Team' : 'Personal'} Site`, testId('orgmenu-title')), + menuSubHeader( + `${this._appModel.isTeamSite ? 'Team' : 'Personal'} Site` + + (this._appModel.isLegacySite ? ' (Legacy)' : ''), + testId('orgmenu-title'), + ), menuItemLink(urlState().setLinkUrl({}), 'Home Page', testId('orgmenu-home-page')), // Show 'Organization Settings' when on a home page of a valid org. diff --git a/app/client/ui/BillingPage.ts b/app/client/ui/BillingPage.ts index e7943233..4212106a 100644 --- a/app/client/ui/BillingPage.ts +++ b/app/client/ui/BillingPage.ts @@ -10,7 +10,7 @@ import {BillingPlanManagers} from 'app/client/ui/BillingPlanManagers'; import {createForbiddenPage} from 'app/client/ui/errorPages'; import {leftPanelBasic} from 'app/client/ui/LeftPanelCommon'; import {pagePanels} from 'app/client/ui/PagePanels'; -import {NEW_DEAL, showTeamUpgradeConfirmation} from 'app/client/ui/ProductUpgrades'; +import {showTeamUpgradeConfirmation} from 'app/client/ui/ProductUpgrades'; import {createTopBarHome} from 'app/client/ui/TopBar'; import {cssBreadcrumbs, cssBreadcrumbsLink, separator} from 'app/client/ui2018/breadcrumbs'; import {bigBasicButton, bigBasicButtonLink, bigPrimaryButton} from 'app/client/ui2018/buttons'; @@ -45,9 +45,6 @@ export class BillingPage extends Disposable { constructor(private _appModel: AppModel) { super(); - - // TODO: remove once NEW_DEAL is there. Execute for side effect - void NEW_DEAL(); this._appModel.refreshOrgUsage().catch(reportError); } diff --git a/app/client/ui/DocMenu.ts b/app/client/ui/DocMenu.ts index 182787ad..a6faa14b 100644 --- a/app/client/ui/DocMenu.ts +++ b/app/client/ui/DocMenu.ts @@ -71,10 +71,11 @@ function createLoadedDocMenu(owner: IDisposableOwner, home: HomeModel) { workspace ? makeLocalViewSettings(home, workspace.id) : home; return [ - // Hide the sort option only when showing intro. - ((showIntro && page === 'all') ? css.prefSelectors(upgradeButton.showUpgradeButton()) : - // This is float:right element - buildPrefs(viewSettings, {hideSort: showIntro}, upgradeButton.showUpgradeButton()) + buildPrefs( + viewSettings, + // Hide the sort and view options when showing the intro. + {hideSort: showIntro, hideView: showIntro && page === 'all'}, + ['all', 'workspace'].includes(page) ? upgradeButton.showUpgradeButton() : null, ), // Build the pinned docs dom. Builds nothing if the selectedOrg is unloaded. @@ -297,12 +298,16 @@ function buildOtherSites(home: HomeModel) { /** * Build the widget for selecting sort and view mode options. - * If hideSort is true, will hide the sort dropdown: it has no effect on the list of examples, so - * best to hide when those are the only docs shown. + * + * Options hideSort and hideView control which options are shown; they should have no effect + * on the list of examples, so best to hide when those are the only docs shown. */ function buildPrefs( viewSettings: ViewSettings, - options: {hideSort: boolean}, + options: { + hideSort: boolean, + hideView: boolean, + }, ...args: DomArg[]): DomContents { return css.prefSelectors( // The Sort selector. @@ -317,7 +322,7 @@ function buildPrefs( ), // The View selector. - buttonSelect(viewSettings.currentView, [ + options.hideView ? null : buttonSelect(viewSettings.currentView, [ {value: 'icons', icon: 'TypeTable'}, {value: 'list', icon: 'TypeCardList'}, ], diff --git a/app/client/ui/HomeLeftPane.ts b/app/client/ui/HomeLeftPane.ts index 326966ca..39510c0b 100644 --- a/app/client/ui/HomeLeftPane.ts +++ b/app/client/ui/HomeLeftPane.ts @@ -191,7 +191,7 @@ function addMenu(home: HomeModel, creating: Observable): DomElementArg[ dom.cls('disabled', (use) => !roles.canEdit(orgAccess) || !use(home.available)), testId("dm-new-workspace") ), - upgradeText(needUpgrade), + upgradeText(needUpgrade, () => home.app.showUpgradeModal()), ]; } @@ -225,8 +225,12 @@ function workspaceMenu(home: HomeModel, ws: Workspace, renaming: Observable home.app.isPersonal), testId('dm-workspace-access')), - upgradeText(needUpgrade), + upgradeText(needUpgrade, () => home.app.showUpgradeModal()), ]; } diff --git a/app/client/ui/NotifyUI.ts b/app/client/ui/NotifyUI.ts index b1bb6428..8f47bf58 100644 --- a/app/client/ui/NotifyUI.ts +++ b/app/client/ui/NotifyUI.ts @@ -19,9 +19,13 @@ function buildAction(action: NotifyAction, item: Notification, options: IBeaconO const appModel = options.appModel; switch (action) { case 'upgrade': - return dom('a', cssToastAction.cls(''), 'Upgrade Plan', {target: '_blank'}, - {href: commonUrls.plans}); - + if (appModel) { + return cssToastAction('Upgrade Plan', dom.on('click', () => + appModel.showUpgradeModal())); + } else { + return dom('a', cssToastAction.cls(''), 'Upgrade Plan', {target: '_blank'}, + {href: commonUrls.plans}); + } case 'renew': // If already on the billing page, nothing to return. if (urlState().state.get().billing === 'billing') { return null; } diff --git a/app/client/ui2018/menus.ts b/app/client/ui2018/menus.ts index 091be7a0..9aff3a4c 100644 --- a/app/client/ui2018/menus.ts +++ b/app/client/ui2018/menus.ts @@ -1,11 +1,11 @@ import { Command } from 'app/client/components/commands'; import { NeedUpgradeError, reportError } from 'app/client/models/errors'; +import { textButton } from 'app/client/ui2018/buttons'; import { cssCheckboxSquare, cssLabel, cssLabelText } from 'app/client/ui2018/checkbox'; import { colors, testId, vars } from 'app/client/ui2018/cssVars'; import { IconName } from 'app/client/ui2018/IconList'; import { icon } from 'app/client/ui2018/icons'; import { cssSelectBtn } from 'app/client/ui2018/select'; -import { commonUrls } from 'app/common/gristUrls'; import { BindableValue, Computed, dom, DomElementArg, DomElementMethod, IDomArgs, MaybeObsArray, MutableObsArray, Observable, styled } from 'grainjs'; import * as weasel from 'popweasel'; @@ -299,10 +299,10 @@ export function upgradableMenuItem(needUpgrade: boolean, action: () => void, ... } } -export function upgradeText(needUpgrade: boolean) { +export function upgradeText(needUpgrade: boolean, onClick: () => void) { if (!needUpgrade) { return null; } return menuText(dom('span', '* Workspaces are available on team plans. ', - dom('a', {href: commonUrls.plans}, 'Upgrade now'))); + cssUpgradeTextButton('Upgrade now', dom.on('click', () => onClick())))); } /** @@ -584,3 +584,7 @@ const cssCheckboxText = styled(cssLabelText, ` color: ${colors.dark}; white-space: pre; `); + +const cssUpgradeTextButton = styled(textButton, ` + font-size: ${vars.smallFontSize}; +`); diff --git a/app/common/Features.ts b/app/common/Features.ts index dd28b4bb..8079aef1 100644 --- a/app/common/Features.ts +++ b/app/common/Features.ts @@ -68,16 +68,55 @@ export function canAddOrgMembers(features: Features): boolean { return features.maxWorkspacesPerOrg !== 1; } -export const FREE_PERSONAL_PLAN = 'starter'; + +export const PERSONAL_LEGACY_PLAN = 'starter'; +export const PERSONAL_FREE_PLAN = 'personalFree'; export const TEAM_FREE_PLAN = 'teamFree'; export const TEAM_PLAN = 'team'; export const displayPlanName: { [key: string]: string } = { + [PERSONAL_LEGACY_PLAN]: 'Free Personal (Legacy)', + [PERSONAL_FREE_PLAN]: 'Free Personal', [TEAM_FREE_PLAN]: 'Team Free', [TEAM_PLAN]: 'Team' } as const; -// Returns true if `product` is free. -export function isFreeProduct(product: Product): boolean { - return [FREE_PERSONAL_PLAN, TEAM_FREE_PLAN, 'Free'].includes(product?.name); +// Returns true if `planName` is for a personal product. +export function isPersonalPlan(planName: string): boolean { + return isFreePersonalPlan(planName); +} + +// Returns true if `planName` is for a free personal product. +export function isFreePersonalPlan(planName: string): boolean { + return [PERSONAL_LEGACY_PLAN, PERSONAL_FREE_PLAN].includes(planName); +} + +// Returns true if `planName` is for a legacy product. +export function isLegacyPlan(planName: string): boolean { + return isFreeLegacyPlan(planName); +} + +// Returns true if `planName` is for a free legacy product. +export function isFreeLegacyPlan(planName: string): boolean { + return [PERSONAL_LEGACY_PLAN].includes(planName); +} + +// Returns true if `planName` is for a team product. +export function isTeamPlan(planName: string): boolean { + return !isPersonalPlan(planName); +} + +// Returns true if `planName` is for a free team product. +export function isFreeTeamPlan(planName: string): boolean { + return [TEAM_FREE_PLAN].includes(planName); +} + +// Returns true if `planName` is for a free product. +export function isFreePlan(planName: string): boolean { + return ( + isFreePersonalPlan(planName) || + isFreeTeamPlan(planName) || + isFreeLegacyPlan(planName) || + planName === 'Free' + ); } diff --git a/app/common/ShareAnnotator.ts b/app/common/ShareAnnotator.ts index 9783e2d2..81f271e0 100644 --- a/app/common/ShareAnnotator.ts +++ b/app/common/ShareAnnotator.ts @@ -1,5 +1,5 @@ +import { isTeamPlan, Product } from 'app/common/Features'; import { normalizeEmail } from 'app/common/emails'; -import { Features } from 'app/common/Features'; import { PermissionData, PermissionDelta } from 'app/common/UserAPI'; /** @@ -33,7 +33,9 @@ export interface ShareAnnotations { * current shares in place. */ export class ShareAnnotator { - constructor(private _features: Features, private _state: PermissionData) { + private _features = this._product?.features ?? {}; + + constructor(private _product: Product|null, private _state: PermissionData) { } public updateState(state: PermissionData) { @@ -43,7 +45,7 @@ export class ShareAnnotator { public annotateChanges(change: PermissionDelta): ShareAnnotations { const features = this._features; const annotations: ShareAnnotations = { - hasTeam: features.maxWorkspacesPerOrg !== 1, + hasTeam: !this._product || isTeamPlan(this._product.name), users: new Map(), }; if (features.maxSharesPerDocPerRole || features.maxSharesPerWorkspace) { diff --git a/app/common/gristUrls.ts b/app/common/gristUrls.ts index 648a281c..04dbe837 100644 --- a/app/common/gristUrls.ts +++ b/app/common/gristUrls.ts @@ -539,6 +539,9 @@ export interface GristLoadConfig { // String to append to the end of the HTML document.title pageTitleSuffix?: string; + + // TODO: can be removed once new deal is released. + newDeal?: boolean; } export const HideableUiElements = StringUnion("helpCenter", "billing", "templates", "multiSite", "multiAccounts"); diff --git a/app/gen-server/entity/Product.ts b/app/gen-server/entity/Product.ts index ee3c4709..f3a2496b 100644 --- a/app/gen-server/entity/Product.ts +++ b/app/gen-server/entity/Product.ts @@ -1,13 +1,14 @@ -import {Features, FREE_PERSONAL_PLAN, Product as IProduct, TEAM_FREE_PLAN, TEAM_PLAN} from 'app/common/Features'; +import {Features, Product as IProduct, PERSONAL_FREE_PLAN, PERSONAL_LEGACY_PLAN, TEAM_FREE_PLAN, + TEAM_PLAN} from 'app/common/Features'; import {nativeValues} from 'app/gen-server/lib/values'; import * as assert from 'assert'; import {BillingAccount} from 'app/gen-server/entity/BillingAccount'; import {BaseEntity, Column, Connection, Entity, OneToMany, PrimaryGeneratedColumn} from 'typeorm'; /** - * A summary of features used in 'starter' plans. + * A summary of features available in legacy personal sites. */ -export const starterFeatures: Features = { +export const personalLegacyFeatures: Features = { workspaces: true, // no vanity domain maxDocsPerOrg: 10, @@ -27,14 +28,27 @@ export const teamFeatures: Features = { /** * A summary of features available in free team sites. - * At time of writing, this is a placeholder, as free sites are fleshed out. */ export const teamFreeFeatures: Features = { workspaces: true, vanityDomain: true, maxSharesPerWorkspace: 0, // all workspace shares need to be org members. maxSharesPerDoc: 2, - maxDocsPerOrg: 20, + snapshotWindow: { count: 30, unit: 'days' }, + baseMaxRowsPerDocument: 5000, + baseMaxApiUnitsPerDocumentPerDay: 5000, + baseMaxDataSizePerDocument: 5000 * 2 * 1024, // 2KB per row + baseMaxAttachmentsBytesPerDocument: 1 * 1024 * 1024 * 1024, // 1GB + gracePeriodDays: 14, +}; + +/** + * A summary of features available in free personal sites. + */ + export const personalFreeFeatures: Features = { + workspaces: true, + maxSharesPerWorkspace: 0, // workspace sharing is disabled. + maxSharesPerDoc: 2, snapshotWindow: { count: 30, unit: 'days' }, baseMaxRowsPerDocument: 5000, baseMaxApiUnitsPerDocumentPerDay: 5000, @@ -96,12 +110,10 @@ export const PRODUCTS: IProduct[] = [ name: 'stub', features: {}, }, - - // These are products set up in stripe. - // TODO: this is not true anymore + // This is a product for legacy personal accounts/orgs. { - name: FREE_PERSONAL_PLAN, - features: starterFeatures, + name: PERSONAL_LEGACY_PLAN, + features: personalLegacyFeatures, }, { name: 'professional', // deprecated, can be removed once no longer referred to in stripe. @@ -122,6 +134,10 @@ export const PRODUCTS: IProduct[] = [ name: TEAM_FREE_PLAN, features: teamFreeFeatures }, + { + name: PERSONAL_FREE_PLAN, + features: personalFreeFeatures, + }, ]; @@ -130,11 +146,18 @@ export const PRODUCTS: IProduct[] = [ */ export function getDefaultProductNames() { const defaultProduct = process.env.GRIST_DEFAULT_PRODUCT; + // TODO: can be removed once new deal is released. + const personalFreePlan = process.env.NEW_DEAL === 'true' + ? PERSONAL_FREE_PLAN : PERSONAL_LEGACY_PLAN; return { - personal: defaultProduct || FREE_PERSONAL_PLAN, // Personal site start off on a functional plan. - teamInitial: defaultProduct || 'stub', // Team site starts off on a limited plan, requiring subscription. - teamCancel: 'suspended', // Team site that has been 'turned off'. - team: defaultProduct || TEAM_PLAN, // Functional team site. + // Personal site start off on a functional plan. + personal: defaultProduct || personalFreePlan, + // Team site starts off on a limited plan, requiring subscription. + teamInitial: defaultProduct || 'stub', + // Team site that has been 'turned off'. + teamCancel: 'suspended', + // Functional team site. + team: defaultProduct || TEAM_PLAN, teamFree: defaultProduct || TEAM_FREE_PLAN, }; } diff --git a/app/gen-server/lib/HomeDBManager.ts b/app/gen-server/lib/HomeDBManager.ts index 505b485a..d5dfe395 100644 --- a/app/gen-server/lib/HomeDBManager.ts +++ b/app/gen-server/lib/HomeDBManager.ts @@ -35,7 +35,8 @@ import {Group} from "app/gen-server/entity/Group"; import {Login} from "app/gen-server/entity/Login"; import {AccessOption, AccessOptionWithRole, Organization} from "app/gen-server/entity/Organization"; import {Pref} from "app/gen-server/entity/Pref"; -import {getDefaultProductNames, Product, starterFeatures} from "app/gen-server/entity/Product"; +import {getDefaultProductNames, personalFreeFeatures, personalLegacyFeatures, + Product} from "app/gen-server/entity/Product"; import {Secret} from "app/gen-server/entity/Secret"; import {User} from "app/gen-server/entity/User"; import {Workspace} from "app/gen-server/entity/Workspace"; @@ -811,7 +812,7 @@ export class HomeDBManager extends EventEmitter { id: 0, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), - domain: this.mergedOrgDomain(), + domain: this.mergedOrgDomain(), name: 'Anonymous', owner: this.makeFullUser(this.getAnonymousUser()), access: 'viewers', @@ -820,7 +821,8 @@ export class HomeDBManager extends EventEmitter { individual: true, product: { name: 'anonymous', - features: starterFeatures, + features: process.env.NEW_DEAL === 'true' + ? personalFreeFeatures : personalLegacyFeatures, }, isManager: false, inGoodStanding: true, diff --git a/app/server/lib/sendAppPage.ts b/app/server/lib/sendAppPage.ts index 0b6d9e84..85dcebdd 100644 --- a/app/server/lib/sendAppPage.ts +++ b/app/server/lib/sendAppPage.ts @@ -52,6 +52,7 @@ export function makeGristConfig(homeUrl: string|null, extra: Partial