[MM-51233] Updates for Trial Requests (#22620)
* Add new trial form to enrich trial requests with more customer info * Update snapshots * One more addition * Fixes from PR feedback * Fix types, i18n, update e2e tests * Fix linter * Fix i18n? * Fix blank translation * update snapshot * Update snapshot properly * Remove inapplicable test * Fix business email validation only happening once * UX Feedback * Fix linter * Fix linter again, not working locally * FIX LINTER * Move isvalid check until after some fields are set * Fix for overlapping modals * Fix linter * UX feedback * UX Feedback * Fix typo in error modal * [MM-51551] Add new Trial Form to Playbooks trial requests (#22650) * Playbooks start trial entrypoints opens new trial form modal * Fix style --------- Co-authored-by: Mattermost Build <build@mattermost.com> * [MM-51347] Trial form error modal for embargoed and air gapped entities (#22656) * Playbooks start trial entrypoints opens new trial form modal * Add support for air gapped environments when making trial requests * Add specific handling for embargoed entities * undo some code * Fix linter * Fix types * Fix style * Updates because TE has to upgrade to E0 before it can activate a trial --------- Co-authored-by: Mattermost Build <build@mattermost.com> --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
@@ -334,7 +334,7 @@ describe('Pricing modal', () => {
|
||||
|
||||
// * Check that enterprise card action button shows Try free for 30 days
|
||||
cy.get('#pricingModal').should('be.visible');
|
||||
cy.get('#start_cloud_trial_btn').contains('Try free for 30 days');
|
||||
cy.get('#start_cloud_trial_btn').contains('Start trial');
|
||||
});
|
||||
|
||||
it('should open pricing modal when Upgrade button clicked while in enterprise trial sku', () => {
|
||||
@@ -365,7 +365,7 @@ describe('Pricing modal', () => {
|
||||
|
||||
// * Check that enterprise card action button is disabled
|
||||
cy.get('#enterprise > .bottom > .bottom_container').should('be.visible');
|
||||
cy.get('#start_cloud_trial_btn').contains('Try free for 30 days');
|
||||
cy.get('#start_cloud_trial_btn').contains('Start trial');
|
||||
cy.get('#enterprise > .bottom > .bottom_container').should('be.visible');
|
||||
cy.get('#start_cloud_trial_btn').should('be.disabled');
|
||||
});
|
||||
|
||||
@@ -102,7 +102,7 @@ describe('Self hosted Pricing modal', () => {
|
||||
// * Check that enteprise trial button is available
|
||||
cy.get('#pricingModal').should('be.visible');
|
||||
cy.get('#enterprise').should('be.visible');
|
||||
cy.get('#start_trial_btn').should('not.be.disabled').contains('Try free for 30 days');
|
||||
cy.get('#start_trial_btn').should('not.be.disabled').contains('Start trial');
|
||||
});
|
||||
|
||||
it('Upgrade button should open pricing modal admin users when the server has requested a trial before on free plan', () => {
|
||||
|
||||
@@ -53,7 +53,7 @@ describe('Feature discovery cloud', () => {
|
||||
|
||||
const testForTrialButton = () => {
|
||||
cy.get('#start_cloud_trial_btn').should('exist');
|
||||
cy.get('#start_cloud_trial_btn').contains('Try free for 30 days');
|
||||
cy.get('#start_cloud_trial_btn').contains('Start trial');
|
||||
};
|
||||
|
||||
const testForUpgradeToProfessionalOption = () => {
|
||||
|
||||
@@ -7812,7 +7812,23 @@ func (c *Client4) GetChannelMemberCountsByGroup(channelID string, includeTimezon
|
||||
return ch, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
func (c *Client4) RequestTrialLicenseWithExtraFields(trialRequest *TrialLicenseRequest) (*Response, error) {
|
||||
b, err := json.Marshal(trialRequest)
|
||||
if err != nil {
|
||||
return nil, NewAppError("RequestTrialLicense", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
r, err := c.DoAPIPost("/trial-license", string(b))
|
||||
if err != nil {
|
||||
return BuildResponse(r), err
|
||||
}
|
||||
|
||||
defer closeBody(r)
|
||||
return BuildResponse(r), nil
|
||||
}
|
||||
|
||||
// RequestTrialLicense will request a trial license and install it in the server
|
||||
// DEPRECATED - USE RequestTrialLicenseWithExtraFields (this method remains for backwards compatibility)
|
||||
func (c *Client4) RequestTrialLicense(users int) (*Response, error) {
|
||||
b, err := json.Marshal(map[string]any{"users": users, "terms_accepted": true})
|
||||
if err != nil {
|
||||
|
||||
@@ -84,6 +84,48 @@ type TrialLicenseRequest struct {
|
||||
Users int `json:"users"`
|
||||
TermsAccepted bool `json:"terms_accepted"`
|
||||
ReceiveEmailsAccepted bool `json:"receive_emails_accepted"`
|
||||
ContactName string `json:"contact_name"`
|
||||
ContactEmail string `json:"contact_email"`
|
||||
CompanyName string `json:"company_name"`
|
||||
CompanyCountry string `json:"company_country"`
|
||||
CompanySize string `json:"company_size"`
|
||||
}
|
||||
|
||||
// If any of the below fields are set, this is not a legacy request, and all fields should be validated
|
||||
func (tlr *TrialLicenseRequest) IsLegacy() bool {
|
||||
return tlr.CompanyCountry == "" && tlr.CompanyName == "" && tlr.CompanySize == "" && tlr.ContactName == ""
|
||||
}
|
||||
|
||||
func (tlr *TrialLicenseRequest) IsValid() bool {
|
||||
if !tlr.TermsAccepted {
|
||||
return false
|
||||
}
|
||||
|
||||
if tlr.Email == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if tlr.Users <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if tlr.CompanyCountry == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if tlr.CompanyName == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if tlr.CompanySize == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if tlr.ContactName == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type Features struct {
|
||||
|
||||
@@ -200,6 +200,100 @@ func TestLicenseRecordPreSave(t *testing.T) {
|
||||
assert.NotZero(t, lr.CreateAt)
|
||||
}
|
||||
|
||||
func TestIsLegacyTrialRequest(t *testing.T) {
|
||||
legacyTr := &TrialLicenseRequest{
|
||||
Email: "test@mattermost.com",
|
||||
TermsAccepted: true,
|
||||
SiteURL: "https://mattermost.com",
|
||||
SiteName: "Mattermost",
|
||||
Users: 100,
|
||||
}
|
||||
t.Run("legacy trial request", func(t *testing.T) {
|
||||
assert.True(t, legacyTr.IsLegacy())
|
||||
})
|
||||
|
||||
t.Run("legacy trial request with any non-legacy field set is not a legacy request", func(t *testing.T) {
|
||||
legacyTr.CompanyCountry = "US"
|
||||
assert.False(t, legacyTr.IsLegacy())
|
||||
legacyTr.CompanyCountry = ""
|
||||
legacyTr.CompanyName = "test company"
|
||||
assert.False(t, legacyTr.IsLegacy())
|
||||
legacyTr.CompanyName = ""
|
||||
legacyTr.CompanySize = "50-100"
|
||||
assert.False(t, legacyTr.IsLegacy())
|
||||
legacyTr.CompanySize = ""
|
||||
legacyTr.ContactName = "test user"
|
||||
assert.False(t, legacyTr.IsLegacy())
|
||||
legacyTr.ContactName = ""
|
||||
assert.True(t, legacyTr.IsLegacy())
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestTrialLicenseRequestIsValid(t *testing.T) {
|
||||
validTlr := &TrialLicenseRequest{
|
||||
Email: "test@test.com",
|
||||
Users: 100,
|
||||
CompanyCountry: "US",
|
||||
CompanyName: "Test Company",
|
||||
CompanySize: "50-100",
|
||||
ContactName: "Test User",
|
||||
TermsAccepted: true,
|
||||
}
|
||||
|
||||
resetBaseRequest := func() {
|
||||
validTlr = &TrialLicenseRequest{
|
||||
Email: "test@test.com",
|
||||
Users: 100,
|
||||
CompanyCountry: "US",
|
||||
CompanyName: "Test Company",
|
||||
CompanySize: "50-100",
|
||||
ContactName: "Test User",
|
||||
TermsAccepted: true,
|
||||
}
|
||||
}
|
||||
t.Run("valid request", func(t *testing.T) {
|
||||
resetBaseRequest()
|
||||
assert.Equal(t, true, validTlr.IsValid())
|
||||
})
|
||||
|
||||
t.Run("no terms", func(t *testing.T) {
|
||||
resetBaseRequest()
|
||||
validTlr.TermsAccepted = false
|
||||
assert.Equal(t, false, validTlr.IsValid())
|
||||
})
|
||||
|
||||
t.Run("no email", func(t *testing.T) {
|
||||
resetBaseRequest()
|
||||
validTlr.Email = ""
|
||||
assert.Equal(t, false, validTlr.IsValid())
|
||||
})
|
||||
|
||||
t.Run("no CompanyCountry", func(t *testing.T) {
|
||||
resetBaseRequest()
|
||||
validTlr.CompanyCountry = ""
|
||||
assert.Equal(t, false, validTlr.IsValid())
|
||||
})
|
||||
|
||||
t.Run("no CompanyName", func(t *testing.T) {
|
||||
resetBaseRequest()
|
||||
validTlr.CompanyName = ""
|
||||
assert.Equal(t, false, validTlr.IsValid())
|
||||
})
|
||||
|
||||
t.Run("no CompanySize", func(t *testing.T) {
|
||||
resetBaseRequest()
|
||||
validTlr.CompanySize = ""
|
||||
assert.Equal(t, false, validTlr.IsValid())
|
||||
})
|
||||
|
||||
t.Run("Bad User Count", func(t *testing.T) {
|
||||
resetBaseRequest()
|
||||
validTlr.Users = 0
|
||||
assert.Equal(t, false, validTlr.IsValid())
|
||||
})
|
||||
}
|
||||
|
||||
func TestLicense_IsTrialLicense(t *testing.T) {
|
||||
t.Run("detect trial license directly from the flag", func(t *testing.T) {
|
||||
license := &License{
|
||||
|
||||
@@ -246,16 +246,6 @@ func validateBusinessEmail(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.Channels().License().IsCloud() {
|
||||
c.Err = model.NewAppError("Api4.validateBusinessEmail", "api.cloud.license_error", nil, "", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleWriteBilling) {
|
||||
c.SetPermissionError(model.PermissionSysconsoleWriteBilling)
|
||||
return
|
||||
}
|
||||
|
||||
user, appErr := c.App.GetUser(c.AppContext.Session().UserId)
|
||||
if appErr != nil {
|
||||
c.Err = model.NewAppError("Api4.validateBusinessEmail", "api.cloud.request_error", nil, "", http.StatusForbidden).Wrap(appErr)
|
||||
|
||||
@@ -328,30 +328,6 @@ func Test_requestTrial(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_validateBusinessEmail(t *testing.T) {
|
||||
t.Run("Returns forbidden for non admin executors", func(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.Client.Login(th.BasicUser.Email, th.BasicUser.Password)
|
||||
|
||||
invalidEmail := model.ValidateBusinessEmailRequest{Email: "invalid@gmail.com"}
|
||||
|
||||
th.App.Srv().SetLicense(model.NewTestLicense("cloud"))
|
||||
|
||||
cloud := mocks.CloudInterface{}
|
||||
|
||||
cloud.Mock.On("ValidateBusinessEmail", th.SystemAdminUser.Id, invalidEmail.Email).Return(errors.New("invalid email"))
|
||||
|
||||
cloudImpl := th.App.Srv().Cloud
|
||||
defer func() {
|
||||
th.App.Srv().Cloud = cloudImpl
|
||||
}()
|
||||
th.App.Srv().Cloud = &cloud
|
||||
|
||||
res, err := th.Client.ValidateBusinessEmail(&invalidEmail)
|
||||
require.Error(t, err)
|
||||
require.Equal(t, http.StatusForbidden, res.StatusCode, "403")
|
||||
})
|
||||
|
||||
t.Run("Returns forbidden for invalid business email", func(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
|
||||
@@ -210,11 +210,7 @@ func requestTrialLicense(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var trialRequest struct {
|
||||
Users int `json:"users"`
|
||||
TermsAccepted bool `json:"terms_accepted"`
|
||||
ReceiveEmailsAccepted bool `json:"receive_emails_accepted"`
|
||||
}
|
||||
var trialRequest *model.TrialLicenseRequest
|
||||
|
||||
b, readErr := io.ReadAll(r.Body)
|
||||
if readErr != nil {
|
||||
@@ -223,8 +219,16 @@ func requestTrialLicense(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
json.Unmarshal(b, &trialRequest)
|
||||
|
||||
if err := c.App.Channels().RequestTrialLicense(c.AppContext.Session().UserId, trialRequest.Users, trialRequest.TermsAccepted, trialRequest.ReceiveEmailsAccepted); err != nil {
|
||||
c.Err = err
|
||||
var appErr *model.AppError
|
||||
// If any of the newly supported trial request fields are set (ie, not a legacy request), process this as a new trial request (requiring the new fields) otherwise fall back on the old method.
|
||||
if !trialRequest.IsLegacy() {
|
||||
appErr = c.App.Channels().RequestTrialLicenseWithExtraFields(c.AppContext.Session().UserId, trialRequest)
|
||||
} else {
|
||||
appErr = c.App.Channels().RequestTrialLicense(c.AppContext.Session().UserId, trialRequest.Users, trialRequest.TermsAccepted, trialRequest.ReceiveEmailsAccepted)
|
||||
}
|
||||
|
||||
if appErr != nil {
|
||||
c.Err = appErr
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -223,6 +223,154 @@ func TestRemoveLicenseFile(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestRequestTrialLicenseWithExtraFields(t *testing.T) {
|
||||
th := Setup(t)
|
||||
defer th.TearDown()
|
||||
|
||||
licenseManagerMock := &mocks.LicenseInterface{}
|
||||
licenseManagerMock.On("CanStartTrial").Return(true, nil)
|
||||
th.App.Srv().Platform().SetLicenseManager(licenseManagerMock)
|
||||
cloud := mocks.CloudInterface{}
|
||||
|
||||
cloudImpl := th.App.Srv().Cloud
|
||||
defer func() {
|
||||
th.App.Srv().Cloud = cloudImpl
|
||||
}()
|
||||
th.App.Srv().Cloud = &cloud
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.SiteURL = "http://localhost:8065/" })
|
||||
nUsers := 1
|
||||
validTrialRequest := &model.TrialLicenseRequest{
|
||||
Email: "test@mattermost.com",
|
||||
Users: nUsers,
|
||||
TermsAccepted: true,
|
||||
CompanyCountry: "US",
|
||||
CompanyName: "mattermost",
|
||||
CompanySize: "1-10",
|
||||
ContactName: "Matter Most",
|
||||
}
|
||||
|
||||
t.Run("permission denied", func(t *testing.T) {
|
||||
resp, err := th.Client.RequestTrialLicenseWithExtraFields(&model.TrialLicenseRequest{})
|
||||
require.Error(t, err)
|
||||
CheckForbiddenStatus(t, resp)
|
||||
})
|
||||
|
||||
t.Run("trial license user count less than current users", func(t *testing.T) {
|
||||
license := model.NewTestLicense()
|
||||
license.Features.Users = model.NewInt(nUsers)
|
||||
licenseJSON, jsonErr := json.Marshal(license)
|
||||
require.NoError(t, jsonErr)
|
||||
testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
res.WriteHeader(http.StatusOK)
|
||||
response := map[string]string{
|
||||
"license": string(licenseJSON),
|
||||
}
|
||||
err := json.NewEncoder(res).Encode(response)
|
||||
require.NoError(t, err)
|
||||
}))
|
||||
defer testServer.Close()
|
||||
|
||||
mockLicenseValidator := mocks2.LicenseValidatorIface{}
|
||||
defer testutils.ResetLicenseValidator()
|
||||
|
||||
mockLicenseValidator.On("ValidateLicense", mock.Anything).Return(true, string(licenseJSON))
|
||||
utils.LicenseValidator = &mockLicenseValidator
|
||||
licenseManagerMock := &mocks.LicenseInterface{}
|
||||
licenseManagerMock.On("CanStartTrial").Return(true, nil).Once()
|
||||
th.App.Srv().Platform().SetLicenseManager(licenseManagerMock)
|
||||
originalCwsUrl := *th.App.Srv().Config().CloudSettings.CWSURL
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.CloudSettings.CWSURL = testServer.URL })
|
||||
defer func(requestTrialURL string) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.CloudSettings.CWSURL = requestTrialURL })
|
||||
}(originalCwsUrl)
|
||||
|
||||
cloud.On("ValidateBusinessEmail", mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
resp, err := th.SystemAdminClient.RequestTrialLicenseWithExtraFields(validTrialRequest)
|
||||
CheckErrorID(t, err, "api.license.add_license.unique_users.app_error")
|
||||
CheckBadRequestStatus(t, resp)
|
||||
})
|
||||
|
||||
t.Run("returns status 451 when it receives status 451", func(t *testing.T) {
|
||||
|
||||
license := model.NewTestLicense()
|
||||
license.Features.Users = model.NewInt(nUsers)
|
||||
licenseJSON, jsonErr := json.Marshal(license)
|
||||
require.NoError(t, jsonErr)
|
||||
testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
res.WriteHeader(http.StatusUnavailableForLegalReasons)
|
||||
}))
|
||||
defer testServer.Close()
|
||||
|
||||
mockLicenseValidator := mocks2.LicenseValidatorIface{}
|
||||
defer testutils.ResetLicenseValidator()
|
||||
|
||||
mockLicenseValidator.On("ValidateLicense", mock.Anything).Return(true, string(licenseJSON))
|
||||
utils.LicenseValidator = &mockLicenseValidator
|
||||
licenseManagerMock := &mocks.LicenseInterface{}
|
||||
licenseManagerMock.On("CanStartTrial").Return(true, nil).Once()
|
||||
th.App.Srv().Platform().SetLicenseManager(licenseManagerMock)
|
||||
|
||||
originalCwsUrl := *th.App.Srv().Config().CloudSettings.CWSURL
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.CloudSettings.CWSURL = testServer.URL })
|
||||
defer func(requestTrialURL string) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.CloudSettings.CWSURL = requestTrialURL })
|
||||
}(originalCwsUrl)
|
||||
|
||||
resp, err := th.SystemAdminClient.RequestTrialLicenseWithExtraFields(validTrialRequest)
|
||||
require.Error(t, err)
|
||||
require.Equal(t, resp.StatusCode, 451)
|
||||
})
|
||||
|
||||
t.Run("returns status 400 if request is a mix of legacy and new fields", func(t *testing.T) {
|
||||
validTrialRequest.CompanyCountry = ""
|
||||
validTrialRequest.Users = 100
|
||||
defer func() { validTrialRequest.CompanyCountry = "US" }()
|
||||
license := model.NewTestLicense()
|
||||
license.Features.Users = model.NewInt(nUsers)
|
||||
licenseJSON, jsonErr := json.Marshal(license)
|
||||
require.NoError(t, jsonErr)
|
||||
testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
res.WriteHeader(http.StatusOK)
|
||||
response := map[string]string{
|
||||
"license": string(licenseJSON),
|
||||
}
|
||||
err := json.NewEncoder(res).Encode(response)
|
||||
require.NoError(t, err)
|
||||
}))
|
||||
defer testServer.Close()
|
||||
|
||||
mockLicenseValidator := mocks2.LicenseValidatorIface{}
|
||||
defer testutils.ResetLicenseValidator()
|
||||
|
||||
mockLicenseValidator.On("ValidateLicense", mock.Anything).Return(true, string(licenseJSON))
|
||||
utils.LicenseValidator = &mockLicenseValidator
|
||||
licenseManagerMock := &mocks.LicenseInterface{}
|
||||
licenseManagerMock.On("CanStartTrial").Return(true, nil).Once()
|
||||
th.App.Srv().Platform().SetLicenseManager(licenseManagerMock)
|
||||
originalCwsUrl := *th.App.Srv().Config().CloudSettings.CWSURL
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.CloudSettings.CWSURL = testServer.URL })
|
||||
defer func(requestTrialURL string) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.CloudSettings.CWSURL = requestTrialURL })
|
||||
}(originalCwsUrl)
|
||||
|
||||
cloud.On("ValidateBusinessEmail", mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
resp, err := th.SystemAdminClient.RequestTrialLicenseWithExtraFields(validTrialRequest)
|
||||
CheckErrorID(t, err, "api.license.request-trial.bad-request")
|
||||
CheckBadRequestStatus(t, resp)
|
||||
})
|
||||
|
||||
th.App.Srv().Platform().SetLicenseManager(nil)
|
||||
t.Run("trial license should fail if LicenseManager is nil", func(t *testing.T) {
|
||||
resp, err := th.SystemAdminClient.RequestTrialLicenseWithExtraFields(validTrialRequest)
|
||||
CheckErrorID(t, err, "api.license.upgrade_needed.app_error")
|
||||
CheckForbiddenStatus(t, resp)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestRequestTrialLicense(t *testing.T) {
|
||||
th := Setup(t)
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -30,6 +30,7 @@ const ServerKey product.ServiceKey = "server"
|
||||
type licenseSvc interface {
|
||||
GetLicense() *model.License
|
||||
RequestTrialLicense(requesterID string, users int, termsAccepted bool, receiveEmailsAccepted bool) *model.AppError
|
||||
RequestTrialLicenseWithExtraFields(requesterID string, trialRequest *model.TrialLicenseRequest) *model.AppError
|
||||
}
|
||||
|
||||
// Channels contains all channels related state.
|
||||
@@ -323,6 +324,10 @@ func (ch *Channels) License() *model.License {
|
||||
return ch.licenseSvc.GetLicense()
|
||||
}
|
||||
|
||||
func (ch *Channels) RequestTrialLicenseWithExtraFields(requesterID string, trialRequest *model.TrialLicenseRequest) *model.AppError {
|
||||
return ch.licenseSvc.RequestTrialLicenseWithExtraFields(requesterID, trialRequest)
|
||||
}
|
||||
|
||||
func (ch *Channels) RequestTrialLicense(requesterID string, users int, termsAccepted bool, receiveEmailsAccepted bool) *model.AppError {
|
||||
return ch.licenseSvc.RequestTrialLicense(requesterID, users, termsAccepted,
|
||||
receiveEmailsAccepted)
|
||||
|
||||
@@ -36,6 +36,51 @@ func (w *licenseWrapper) GetLicense() *model.License {
|
||||
return w.srv.License()
|
||||
}
|
||||
|
||||
func (w *licenseWrapper) RequestTrialLicenseWithExtraFields(requesterID string, trialRequest *model.TrialLicenseRequest) *model.AppError {
|
||||
if *w.srv.platform.Config().ExperimentalSettings.RestrictSystemAdmin {
|
||||
return model.NewAppError("RequestTrialLicense", "api.restricted_system_admin", nil, "", http.StatusForbidden)
|
||||
}
|
||||
|
||||
requester, err := w.srv.userService.GetUser(requesterID)
|
||||
if err != nil {
|
||||
var nfErr *store.ErrNotFound
|
||||
switch {
|
||||
case errors.As(err, &nfErr):
|
||||
return model.NewAppError("RequestTrialLicense", MissingAccountError, nil, "", http.StatusNotFound).Wrap(err)
|
||||
default:
|
||||
return model.NewAppError("RequestTrialLicense", "app.user.get_by_username.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
if w.srv.Cloud.ValidateBusinessEmail(requesterID, trialRequest.ContactEmail) != nil {
|
||||
return model.NewAppError("RequestTrialLicense", "api.license.request-trial.bad-request.business-email", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// Create a new struct only using the fields from the request that are allowed to be set by the client
|
||||
sanitizedRequest := &model.TrialLicenseRequest{
|
||||
ServerID: w.srv.TelemetryId(),
|
||||
Name: requester.GetDisplayName(model.ShowFullName),
|
||||
Email: requester.Email,
|
||||
SiteName: *w.srv.platform.Config().TeamSettings.SiteName,
|
||||
SiteURL: *w.srv.platform.Config().ServiceSettings.SiteURL,
|
||||
Users: trialRequest.Users,
|
||||
TermsAccepted: trialRequest.TermsAccepted,
|
||||
ReceiveEmailsAccepted: trialRequest.ReceiveEmailsAccepted,
|
||||
ContactName: trialRequest.ContactName,
|
||||
ContactEmail: trialRequest.ContactEmail,
|
||||
CompanyName: trialRequest.CompanyName,
|
||||
CompanySize: trialRequest.CompanySize,
|
||||
CompanyCountry: trialRequest.CompanyCountry,
|
||||
}
|
||||
|
||||
if !sanitizedRequest.IsValid() {
|
||||
return model.NewAppError("RequestTrialLicense", "api.license.request-trial.bad-request", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
return w.srv.platform.RequestTrialLicense(sanitizedRequest)
|
||||
}
|
||||
|
||||
// DEPRECATED - use RequestTrialLicenseWithExtraFields instead. This function remains to support the Plugin API.
|
||||
func (w *licenseWrapper) RequestTrialLicense(requesterID string, users int, termsAccepted bool, receiveEmailsAccepted bool) *model.AppError {
|
||||
if *w.srv.platform.Config().ExperimentalSettings.RestrictSystemAdmin {
|
||||
return model.NewAppError("RequestTrialLicense", "api.restricted_system_admin", nil, "", http.StatusForbidden)
|
||||
|
||||
@@ -2049,6 +2049,10 @@
|
||||
"id": "api.license.request-trial.bad-request",
|
||||
"translation": "The number of users requested is not correct."
|
||||
},
|
||||
{
|
||||
"id": "api.license.request-trial.bad-request.business-email",
|
||||
"translation": "Invalid business email for trial"
|
||||
},
|
||||
{
|
||||
"id": "api.license.request-trial.bad-request.terms-not-accepted",
|
||||
"translation": "You must accept the Mattermost Software Evaluation Agreement and Privacy Policy to request a license."
|
||||
|
||||
@@ -451,12 +451,12 @@ export function ping() {
|
||||
};
|
||||
}
|
||||
|
||||
export function requestTrialLicense(users, termsAccepted, receiveEmailsAccepted, page) {
|
||||
export function requestTrialLicense(requestLicenseBody, page) {
|
||||
return async () => {
|
||||
try {
|
||||
trackEvent('api', 'api_request_trial_license', {from_page: page});
|
||||
const response = await Client4.requestTrialLicense({users, terms_accepted: termsAccepted, receive_emails_accepted: receiveEmailsAccepted});
|
||||
|
||||
const response = await Client4.requestTrialLicense(requestLicenseBody);
|
||||
return {data: response};
|
||||
} catch (e) {
|
||||
// In the event that the status code returned is 451, this request has been blocked because it originated from an embargoed country_dropdown
|
||||
|
||||
@@ -3,3 +3,77 @@
|
||||
transform: translateY(50%);
|
||||
}
|
||||
}
|
||||
|
||||
.ResultModal__small {
|
||||
.modal-header {
|
||||
.close {
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus,
|
||||
&:active:focus {
|
||||
background-color: rgba(var(--center-channel-color-rgb), 0.08);
|
||||
color: rgba(var(--center-channel-color-rgb), 0.72);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
top: 26px;
|
||||
right: 26px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 4px;
|
||||
color: rgba(var(--center-channel-color-rgb), 0.56) !important;
|
||||
font-family:
|
||||
'Open Sans',
|
||||
sans-serif;
|
||||
font-size: 32px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 600px;
|
||||
height: 360px;
|
||||
border: 1px solid rgba(var(--center-channel-color-rgb), 0.08);
|
||||
margin: auto;
|
||||
border-radius: 8px;
|
||||
transform: translate(-50%, -50%) !important;
|
||||
|
||||
.modal-header {
|
||||
border: none;
|
||||
background: var(--center-channel-bg) !important;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
height: 100%;
|
||||
border-radius: 8px;
|
||||
|
||||
.IconMessage__svg-wrapper {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.IconMessage-h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.IconMessage-sub {
|
||||
max-width: 504px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.IconMessage-buttons {
|
||||
justify-content: right;
|
||||
padding-right: 24px;
|
||||
margin-top: 60px;
|
||||
|
||||
.btn {
|
||||
font-family: 'Open sans', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,12 @@ import {useOpenCloudZendeskSupportForm} from 'components/common/hooks/useOpenZen
|
||||
import {closeModal} from 'actions/views/modals';
|
||||
import {isModalOpen} from 'selectors/views/modals';
|
||||
import {GlobalState} from 'types/store';
|
||||
import {Modal} from 'react-bootstrap';
|
||||
|
||||
import './result_modal.scss';
|
||||
|
||||
type Props = {
|
||||
type?: string;
|
||||
onHide?: () => void;
|
||||
icon: JSX.Element;
|
||||
title: JSX.Element;
|
||||
@@ -28,47 +30,77 @@ type Props = {
|
||||
ignoreExit: boolean;
|
||||
};
|
||||
|
||||
export default function ResultModal(props: Props) {
|
||||
export default function ResultModal({type, icon, title, subtitle, primaryButtonText, primaryButtonHandler, identifier, contactSupportButtonVisible, resultType, ignoreExit, onHide}: Props) {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [openContactSupport] = useOpenCloudZendeskSupportForm('Delete workspace', '');
|
||||
|
||||
const isResultModalOpen = useSelector((state: GlobalState) =>
|
||||
isModalOpen(state, props.identifier),
|
||||
isModalOpen(state, identifier),
|
||||
);
|
||||
|
||||
const onHide = () => {
|
||||
dispatch(closeModal(props.identifier));
|
||||
if (typeof props.onHide === 'function') {
|
||||
props.onHide();
|
||||
}
|
||||
const handleHide = () => {
|
||||
dispatch(closeModal(identifier));
|
||||
onHide?.();
|
||||
};
|
||||
|
||||
const modalType = `delete-workspace-result_modal__${props.resultType}`;
|
||||
const modalType = `delete-workspace-result_modal__${resultType}`;
|
||||
if (type === 'small') {
|
||||
return (
|
||||
<Modal
|
||||
className='ResultModal__small'
|
||||
show={isResultModalOpen}
|
||||
onHide={handleHide}
|
||||
>
|
||||
<Modal.Header closeButton={true}/>
|
||||
<div className={modalType}>
|
||||
<IconMessage
|
||||
formattedTitle={title}
|
||||
formattedSubtitle={subtitle}
|
||||
error={false}
|
||||
icon={icon}
|
||||
formattedButtonText={primaryButtonText}
|
||||
buttonHandler={primaryButtonHandler}
|
||||
className={'success'}
|
||||
formattedTertiaryButonText={
|
||||
contactSupportButtonVisible ?
|
||||
<FormattedMessage
|
||||
id={'admin.billing.deleteWorkspace.resultModal.ContactSupport'}
|
||||
defaultMessage={'Contact Support'}
|
||||
/> :
|
||||
undefined
|
||||
}
|
||||
tertiaryButtonHandler={contactSupportButtonVisible ? openContactSupport : undefined}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FullScreenModal
|
||||
show={isResultModalOpen}
|
||||
onClose={onHide}
|
||||
ignoreExit={props.ignoreExit}
|
||||
onClose={handleHide}
|
||||
ignoreExit={ignoreExit}
|
||||
>
|
||||
<div className={modalType}>
|
||||
<IconMessage
|
||||
formattedTitle={props.title}
|
||||
formattedSubtitle={props.subtitle}
|
||||
formattedTitle={title}
|
||||
formattedSubtitle={subtitle}
|
||||
error={false}
|
||||
icon={props.icon}
|
||||
formattedButtonText={props.primaryButtonText}
|
||||
buttonHandler={props.primaryButtonHandler}
|
||||
icon={icon}
|
||||
formattedButtonText={primaryButtonText}
|
||||
buttonHandler={primaryButtonHandler}
|
||||
className={'success'}
|
||||
formattedTertiaryButonText={
|
||||
props.contactSupportButtonVisible ? (
|
||||
contactSupportButtonVisible ? (
|
||||
|
||||
<FormattedMessage
|
||||
id={'admin.billing.deleteWorkspace.resultModal.ContactSupport'}
|
||||
defaultMessage={'Contact Support'}
|
||||
/>) : undefined
|
||||
}
|
||||
tertiaryButtonHandler={props.contactSupportButtonVisible ? openContactSupport : undefined}
|
||||
tertiaryButtonHandler={contactSupportButtonVisible ? openContactSupport : undefined}
|
||||
/>
|
||||
</div>
|
||||
</FullScreenModal>
|
||||
|
||||
@@ -208,117 +208,6 @@ exports[`components/admin_console/license_settings/LicenseSettings load screen w
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/admin_console/license_settings/LicenseSettings should match snapshot after starting trial and removing license 1`] = `
|
||||
<div
|
||||
className="wrapper--fixed"
|
||||
>
|
||||
<FormattedAdminHeader
|
||||
defaultMessage="Edition and License"
|
||||
id="admin.license.title"
|
||||
values={Object {}}
|
||||
/>
|
||||
<div
|
||||
className="admin-console__wrapper"
|
||||
>
|
||||
<div
|
||||
className="admin-console__content"
|
||||
>
|
||||
<div
|
||||
className="admin-console__banner_section"
|
||||
>
|
||||
<TrialBanner
|
||||
enterpriseReady={true}
|
||||
gettingTrial={false}
|
||||
gettingTrialError={null}
|
||||
handleRestart={[Function]}
|
||||
handleUpgrade={[Function]}
|
||||
isDisabled={false}
|
||||
openEEModal={[Function]}
|
||||
requestLicense={[Function]}
|
||||
restartError={null}
|
||||
restarting={false}
|
||||
upgradeError={null}
|
||||
upgradingPercentage={0}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="top-wrapper"
|
||||
>
|
||||
<div
|
||||
className="left-panel"
|
||||
>
|
||||
<div
|
||||
className="panel-card"
|
||||
>
|
||||
<Memo(StarterLeftPanel)
|
||||
currentPlan={
|
||||
<div
|
||||
className="current-plan-legend"
|
||||
>
|
||||
<i
|
||||
className="icon-check-circle"
|
||||
/>
|
||||
Current Plan
|
||||
</div>
|
||||
}
|
||||
fileInputRef={
|
||||
Object {
|
||||
"current": null,
|
||||
}
|
||||
}
|
||||
handleChange={[Function]}
|
||||
openEELicenseModal={[Function]}
|
||||
upgradedFromTE={false}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="terms-and-policy"
|
||||
>
|
||||
See also
|
||||
<ExternalLink
|
||||
href="https://mattermost.com/terms-of-use/"
|
||||
id="privacyLink"
|
||||
location="license_settings"
|
||||
>
|
||||
Enterprise Edition Terms of Use
|
||||
</ExternalLink>
|
||||
and
|
||||
<ExternalLink
|
||||
href="https://mattermost.com/privacy-policy/"
|
||||
id="privacyLink"
|
||||
location="license_settings"
|
||||
>
|
||||
Privacy Policy
|
||||
</ExternalLink>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="right-panel"
|
||||
>
|
||||
<div
|
||||
className="panel-card"
|
||||
>
|
||||
<Memo(StarterRightPanel) />
|
||||
</div>
|
||||
<div
|
||||
className="compare-plans-text"
|
||||
>
|
||||
Curious about upgrading?
|
||||
<ExternalLink
|
||||
href="https://mattermost.com/pricing/"
|
||||
id="privacyLink"
|
||||
location="license_settings"
|
||||
>
|
||||
Compare Plans
|
||||
</ExternalLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/admin_console/license_settings/LicenseSettings should match snapshot enterprise build with E10 license 1`] = `
|
||||
<div
|
||||
className="wrapper--fixed"
|
||||
@@ -1187,7 +1076,7 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
||||
<div
|
||||
className="admin-console__banner_section"
|
||||
>
|
||||
<TrialBanner
|
||||
<Component
|
||||
enterpriseReady={true}
|
||||
gettingTrial={false}
|
||||
gettingTrialError={null}
|
||||
@@ -1196,7 +1085,6 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
||||
handleUpgrade={[Function]}
|
||||
isDisabled={false}
|
||||
openEEModal={[Function]}
|
||||
requestLicense={[Function]}
|
||||
restartError={null}
|
||||
restarting={false}
|
||||
upgradeError={null}
|
||||
@@ -1299,7 +1187,7 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
||||
<div
|
||||
className="admin-console__banner_section"
|
||||
>
|
||||
<TrialBanner
|
||||
<Component
|
||||
enterpriseReady={true}
|
||||
gettingTrial={false}
|
||||
gettingTrialError={null}
|
||||
@@ -1308,7 +1196,6 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
||||
handleUpgrade={[Function]}
|
||||
isDisabled={false}
|
||||
openEEModal={[Function]}
|
||||
requestLicense={[Function]}
|
||||
restartError={null}
|
||||
restarting={false}
|
||||
upgradeError={null}
|
||||
@@ -1515,7 +1402,7 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
||||
<div
|
||||
className="admin-console__banner_section"
|
||||
>
|
||||
<TrialBanner
|
||||
<Component
|
||||
enterpriseReady={false}
|
||||
gettingTrial={false}
|
||||
gettingTrialError={null}
|
||||
@@ -1524,7 +1411,6 @@ exports[`components/admin_console/license_settings/LicenseSettings should match
|
||||
handleUpgrade={[Function]}
|
||||
isDisabled={false}
|
||||
openEEModal={[Function]}
|
||||
requestLicense={[Function]}
|
||||
restartError={null}
|
||||
restarting={false}
|
||||
upgradeError={null}
|
||||
|
||||
@@ -167,34 +167,6 @@ describe('components/admin_console/license_settings/LicenseSettings', () => {
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot after starting trial and removing license', async () => {
|
||||
const actions = {
|
||||
...defaultProps.actions,
|
||||
getLicenseConfig: jest.fn(),
|
||||
upgradeToE0: jest.fn(),
|
||||
upgradeToE0Status: jest.fn().mockImplementation(() => Promise.resolve({percentage: 0, error: null})),
|
||||
};
|
||||
const props = {...defaultProps, license: {IsLicensed: 'false'}, prevTrialLicense: {IsLicensed: 'false'}, actions};
|
||||
|
||||
const wrapper = shallow<LicenseSettings>(<LicenseSettings {...props}/>);
|
||||
|
||||
const instance = wrapper.instance();
|
||||
|
||||
// First start trial
|
||||
actions.requestTrialLicense = jest.fn().mockImplementation(() => Promise.resolve({percentage: 1, error: null}));
|
||||
actions.getLicenseConfig = jest.fn().mockImplementation(() => Promise.resolve({}));
|
||||
await instance.requestLicense({preventDefault: jest.fn()} as unknown as React.MouseEvent<HTMLButtonElement>);
|
||||
expect(wrapper.state('gettingTrial')).toBe(false);
|
||||
|
||||
// Then remove license
|
||||
actions.removeLicense = jest.fn().mockImplementation(() => Promise.resolve({percentage: 1, error: null}));
|
||||
actions.getPrevTrialLicense = jest.fn().mockImplementation(() => Promise.resolve({}));
|
||||
await instance.handleRemove({preventDefault: jest.fn()} as unknown as React.MouseEvent<HTMLButtonElement>);
|
||||
expect(wrapper.state('removing')).toBe(false);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot enterprise build with E20 license', () => {
|
||||
const props = {...defaultProps, license: {...defaultProps.license, SkuShortName: LicenseSkus.E20}};
|
||||
const wrapper = shallow<LicenseSettings>(<LicenseSettings {...props}/>);
|
||||
|
||||
@@ -202,23 +202,6 @@ export default class LicenseSettings extends React.PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
requestLicense = async (e?: React.MouseEvent<HTMLButtonElement>) => {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
if (this.state.gettingTrial) {
|
||||
return;
|
||||
}
|
||||
this.setState({gettingTrial: true, gettingTrialError: null});
|
||||
const requestedUsers = Math.max(this.props.totalUsers, 30) || 30;
|
||||
const {error, data} = await this.props.actions.requestTrialLicense(requestedUsers, true, true, 'license');
|
||||
if (error) {
|
||||
this.setState({gettingTrialError: error});
|
||||
}
|
||||
this.setState({gettingTrial: false, gettingTrialResponseCode: data?.status});
|
||||
await this.props.actions.getLicenseConfig();
|
||||
}
|
||||
|
||||
checkRestarted = () => {
|
||||
this.props.actions.ping().then(() => {
|
||||
window.location.reload();
|
||||
@@ -363,7 +346,6 @@ export default class LicenseSettings extends React.PureComponent<Props, State> {
|
||||
isDisabled={isDisabled}
|
||||
gettingTrialResponseCode={this.state.gettingTrialResponseCode}
|
||||
gettingTrialError={this.state.gettingTrialError}
|
||||
requestLicense={this.requestLicense}
|
||||
gettingTrial={this.state.gettingTrial}
|
||||
enterpriseReady={this.props.enterpriseReady}
|
||||
upgradingPercentage={this.state.upgradingPercentage}
|
||||
|
||||
@@ -13,6 +13,8 @@ import {makeGetCategory} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import AlertBanner from 'components/alert_banner';
|
||||
import LoadingWrapper from 'components/widgets/loading/loading_wrapper';
|
||||
import FormattedMarkdownMessage from 'components/formatted_markdown_message';
|
||||
import withOpenStartTrialFormModal from 'components/common/hocs/cloud/with_open_start_trial_form_modal';
|
||||
import {TelemetryProps} from 'components/common/hooks/useOpenPricingModal';
|
||||
|
||||
import {format} from 'utils/markdown';
|
||||
|
||||
@@ -26,13 +28,13 @@ interface TrialBannerProps {
|
||||
isDisabled: boolean;
|
||||
gettingTrialError: string | null;
|
||||
gettingTrialResponseCode: number | null;
|
||||
requestLicense: (e?: React.MouseEvent<HTMLButtonElement>, reload?: boolean) => Promise<void>;
|
||||
gettingTrial: boolean;
|
||||
enterpriseReady: boolean;
|
||||
upgradingPercentage: number;
|
||||
handleUpgrade: () => Promise<void>;
|
||||
upgradeError: string | null;
|
||||
restartError: string | null;
|
||||
openTrialForm?: (telemetryProps?: TelemetryProps) => void;
|
||||
|
||||
handleRestart: () => Promise<void>;
|
||||
|
||||
@@ -72,7 +74,6 @@ const TrialBanner = ({
|
||||
isDisabled,
|
||||
gettingTrialError,
|
||||
gettingTrialResponseCode,
|
||||
requestLicense,
|
||||
gettingTrial,
|
||||
enterpriseReady,
|
||||
upgradingPercentage,
|
||||
@@ -82,6 +83,7 @@ const TrialBanner = ({
|
||||
handleRestart,
|
||||
restarting,
|
||||
openEEModal,
|
||||
openTrialForm,
|
||||
}: TrialBannerProps) => {
|
||||
let trialButton;
|
||||
let upgradeTermsMessage;
|
||||
@@ -119,6 +121,12 @@ const TrialBanner = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handleRequestLicense = () => {
|
||||
if (openTrialForm) {
|
||||
openTrialForm({trackingLocation: 'license_settings.trial_banner'});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
async function savePrefsAndRequestTrial() {
|
||||
await savePrefsRestartedAfterUpgrade();
|
||||
@@ -150,7 +158,7 @@ const TrialBanner = ({
|
||||
const clickedBtn = Unique.CLICKED_UPGRADE_AND_TRIAL_BTN;
|
||||
dispatch(savePreferences(userId, [{category, name: reqLicense, user_id: userId, value: ''}, {category, name: clickedBtn, user_id: userId, value: ''}]));
|
||||
|
||||
requestLicense();
|
||||
handleRequestLicense();
|
||||
}
|
||||
}, [restartedAfterUpgradePrefs, clickedUpgradeAndTrialBtn]);
|
||||
|
||||
@@ -213,7 +221,7 @@ const TrialBanner = ({
|
||||
<button
|
||||
type='button'
|
||||
className='btn btn-primary'
|
||||
onClick={requestLicense}
|
||||
onClick={handleRequestLicense}
|
||||
disabled={isDisabled || gettingTrialError !== null || gettingTrialResponseCode === 451}
|
||||
>
|
||||
{btnText(status)}
|
||||
@@ -372,4 +380,4 @@ const TrialBanner = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default TrialBanner;
|
||||
export default withOpenStartTrialFormModal(TrialBanner);
|
||||
|
||||
@@ -13,8 +13,8 @@ import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general
|
||||
import {makeGetCategory} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {getCurrentUser, isCurrentUserSystemAdmin} from 'mattermost-redux/selectors/entities/users';
|
||||
import {savePreferences} from 'mattermost-redux/actions/preferences';
|
||||
import useOpenStartTrialFormModal from 'components/common/hooks/useOpenStartTrialFormModal';
|
||||
|
||||
import {openModal} from 'actions/views/modals';
|
||||
import {GlobalState} from 'types/store';
|
||||
|
||||
import {
|
||||
@@ -24,13 +24,12 @@ import {
|
||||
ModalIdentifiers,
|
||||
} from 'utils/constants';
|
||||
|
||||
import StartTrialModal from 'components/start_trial_modal';
|
||||
|
||||
import {trackEvent} from 'actions/telemetry_actions';
|
||||
import {isModalOpen} from 'selectors/views/modals';
|
||||
|
||||
const ShowStartTrialModal = () => {
|
||||
const isUserAdmin = useSelector((state: GlobalState) => isCurrentUserSystemAdmin(state));
|
||||
const openStartTrialFormModal = useOpenStartTrialFormModal();
|
||||
|
||||
const dispatch = useDispatch<DispatchFunc>();
|
||||
const getCategory = makeGetCategory();
|
||||
@@ -81,11 +80,7 @@ const ShowStartTrialModal = () => {
|
||||
const hasEnvMoreThan10Users = Number(totalUsers) > userThreshold;
|
||||
const hadAdminDismissedModal = preferences.some((pref: PreferenceType) => pref.name === Constants.TRIAL_MODAL_AUTO_SHOWN && pref.value === TRUE);
|
||||
if (isUserAdmin && !isBenefitsModalOpened && hasEnvMoreThan10Users && hasEnvMoreThan6Hours && !hadAdminDismissedModal && !isLicensedOrPreviousLicensed) {
|
||||
dispatch(openModal({
|
||||
modalId: ModalIdentifiers.START_TRIAL_MODAL,
|
||||
dialogType: StartTrialModal,
|
||||
dialogProps: {onClose: handleOnClose},
|
||||
}));
|
||||
openStartTrialFormModal({trackingLocation: 'show_start_trial_modal'}, handleOnClose);
|
||||
trackEvent(
|
||||
TELEMETRY_CATEGORIES.SELF_HOSTED_START_TRIAL_AUTO_MODAL,
|
||||
'trigger_start_trial_auto_modal',
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {ComponentType} from 'react';
|
||||
|
||||
import useOpenStartTrialFormModal from 'components/common/hooks/useOpenStartTrialFormModal';
|
||||
|
||||
export default function withOpenStartTrialFormModal<T>(WrappedComponent: ComponentType<T>) {
|
||||
return (props: T) => {
|
||||
const openTrialForm = useOpenStartTrialFormModal();
|
||||
return (
|
||||
<WrappedComponent
|
||||
openTrialForm={openTrialForm}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -8,14 +8,16 @@ import {Client4} from 'mattermost-redux/client';
|
||||
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
||||
|
||||
export default function useCWSAvailabilityCheck() {
|
||||
const [canReachCWS, setCanReachCWS] = useState(false);
|
||||
const [canReachCWS, setCanReachCWS] = useState<boolean | undefined>(undefined);
|
||||
const config = useSelector(getConfig);
|
||||
const isEnterpriseReady = config.BuildEnterpriseReady === 'true';
|
||||
useEffect(() => {
|
||||
if (!isEnterpriseReady) {
|
||||
return;
|
||||
}
|
||||
Client4.cwsAvailabilityCheck().then(() => setCanReachCWS(true));
|
||||
Client4.cwsAvailabilityCheck().then(() => {
|
||||
setCanReachCWS(true);
|
||||
}).catch(() => setCanReachCWS(false));
|
||||
}, [isEnterpriseReady]);
|
||||
|
||||
return canReachCWS;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {useDispatch} from 'react-redux';
|
||||
|
||||
import {openModal} from 'actions/views/modals';
|
||||
import {ModalIdentifiers} from 'utils/constants';
|
||||
import StartTrialFormModal from 'components/start_trial_form_modal';
|
||||
import {TelemetryProps} from './useOpenPricingModal';
|
||||
|
||||
export default function useOpenStartTrialFormModal() {
|
||||
const dispatch = useDispatch();
|
||||
return (telemetryProps?: TelemetryProps, onClose?: () => void) => {
|
||||
dispatch(openModal({
|
||||
modalId: ModalIdentifiers.START_TRIAL_FORM_MODAL,
|
||||
dialogType: StartTrialFormModal,
|
||||
dialogProps: {
|
||||
page: telemetryProps?.trackingLocation,
|
||||
onClose,
|
||||
},
|
||||
}));
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,621 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
type SvgProps = {
|
||||
height?: number;
|
||||
width?: number;
|
||||
}
|
||||
|
||||
const AirgappedTrialActivationConfirmSvg = ({height = 474, width = 578}: SvgProps) => (
|
||||
<svg
|
||||
width={width.toString()}
|
||||
height={height.toString()}
|
||||
viewBox='0 0 223 159'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<g clipPath='url(#clip0_754_57919)'>
|
||||
<path
|
||||
d='M17.1671 115.779H174.208C175.364 115.763 176.466 115.281 177.273 114.439C178.081 113.597 178.528 112.464 178.516 111.287V4.46575C178.528 3.28926 178.081 2.15606 177.273 1.3142C176.466 0.47233 175.364 -0.00961028 174.208 -0.026123H17.1671C16.0121 -0.00797133 14.9109 0.474468 14.1039 1.31597C13.297 2.15748 12.8496 3.28974 12.8595 4.46575V111.294C12.8513 112.468 13.2993 113.599 14.1061 114.439C14.9129 115.279 16.0131 115.761 17.1671 115.779Z'
|
||||
fill='#3F4350'
|
||||
/>
|
||||
<path
|
||||
d='M-0.15625 131.141C-0.15625 136.459 4.17094 141.776 9.41922 141.776H181.963C186.909 141.776 191.532 136.471 191.532 131.141H-0.15625Z'
|
||||
fill='#767D93'
|
||||
/>
|
||||
<path
|
||||
d='M175.559 115.779H15.8173L-0.15625 131.141H191.532L175.559 115.779Z'
|
||||
fill='#D1D4DB'
|
||||
/>
|
||||
<path
|
||||
d='M170.782 116.961H20.5884L15.2266 123.46H176.15L170.782 116.961Z'
|
||||
fill='#AFB3C0'
|
||||
/>
|
||||
<path
|
||||
d='M112.155 125.823H79.2272L76.7559 129.369H114.62L112.155 125.823Z'
|
||||
fill='#24262E'
|
||||
/>
|
||||
<g clipPath='url(#clip1_754_57919)'>
|
||||
<rect
|
||||
width='146.725'
|
||||
height='94.5349'
|
||||
transform='translate(22.3262 10.0183)'
|
||||
fill='white'
|
||||
/>
|
||||
<rect
|
||||
x='22.3262'
|
||||
y='10.0183'
|
||||
width='43.7808'
|
||||
height='95.1258'
|
||||
fill='#1E325C'
|
||||
/>
|
||||
<ellipse
|
||||
opacity='0.32'
|
||||
cx='29.4256'
|
||||
cy='17.6994'
|
||||
rx='4.14142'
|
||||
ry='4.1359'
|
||||
fill='white'
|
||||
/>
|
||||
<rect
|
||||
opacity='0.32'
|
||||
x='36.5254'
|
||||
y='16.5176'
|
||||
width='19.5238'
|
||||
height='1.77253'
|
||||
rx='0.886265'
|
||||
fill='white'
|
||||
/>
|
||||
<rect
|
||||
opacity='0.32'
|
||||
x='26.4678'
|
||||
y='27.1528'
|
||||
width='13.0159'
|
||||
height='1.77253'
|
||||
rx='0.886265'
|
||||
fill='white'
|
||||
/>
|
||||
<rect
|
||||
opacity='0.32'
|
||||
x='26.4678'
|
||||
y='83.8738'
|
||||
width='13.0159'
|
||||
height='1.77253'
|
||||
rx='0.886265'
|
||||
fill='white'
|
||||
/>
|
||||
<ellipse
|
||||
opacity='0.32'
|
||||
cx='28.5385'
|
||||
cy='34.5382'
|
||||
rx='2.07071'
|
||||
ry='2.06795'
|
||||
fill='white'
|
||||
/>
|
||||
<rect
|
||||
opacity='0.32'
|
||||
x='32.9756'
|
||||
y='33.6519'
|
||||
width='24.5527'
|
||||
height='1.69648'
|
||||
rx='0.848242'
|
||||
fill='white'
|
||||
/>
|
||||
<ellipse
|
||||
opacity='0.32'
|
||||
cx='28.5385'
|
||||
cy='55.8087'
|
||||
rx='2.07071'
|
||||
ry='2.06795'
|
||||
fill='white'
|
||||
/>
|
||||
<rect
|
||||
opacity='0.32'
|
||||
x='32.9756'
|
||||
y='54.9224'
|
||||
width='24.2569'
|
||||
height='1.77253'
|
||||
rx='0.886265'
|
||||
fill='white'
|
||||
/>
|
||||
<ellipse
|
||||
opacity='0.32'
|
||||
cx='28.5385'
|
||||
cy='41.6285'
|
||||
rx='2.07071'
|
||||
ry='2.06795'
|
||||
fill='white'
|
||||
/>
|
||||
<rect
|
||||
opacity='0.32'
|
||||
x='32.9756'
|
||||
y='40.7422'
|
||||
width='21.2987'
|
||||
height='1.77253'
|
||||
rx='0.886264'
|
||||
fill='white'
|
||||
/>
|
||||
<ellipse
|
||||
opacity='0.32'
|
||||
cx='28.5385'
|
||||
cy='62.8988'
|
||||
rx='2.07071'
|
||||
ry='2.06795'
|
||||
fill='white'
|
||||
/>
|
||||
<rect
|
||||
opacity='0.32'
|
||||
x='32.9756'
|
||||
y='62.0125'
|
||||
width='21.2987'
|
||||
height='1.77253'
|
||||
rx='0.886265'
|
||||
fill='white'
|
||||
/>
|
||||
<ellipse
|
||||
opacity='0.32'
|
||||
cx='28.5385'
|
||||
cy='48.7186'
|
||||
rx='2.07071'
|
||||
ry='2.06795'
|
||||
fill='white'
|
||||
/>
|
||||
<rect
|
||||
opacity='0.32'
|
||||
x='32.9756'
|
||||
y='47.8323'
|
||||
width='27.8067'
|
||||
height='1.77253'
|
||||
rx='0.886265'
|
||||
fill='white'
|
||||
/>
|
||||
<ellipse
|
||||
opacity='0.32'
|
||||
cx='28.5385'
|
||||
cy='69.9888'
|
||||
rx='2.07071'
|
||||
ry='2.06795'
|
||||
fill='white'
|
||||
/>
|
||||
<rect
|
||||
opacity='0.32'
|
||||
x='32.9756'
|
||||
y='69.1025'
|
||||
width='27.8067'
|
||||
height='1.77253'
|
||||
rx='0.886265'
|
||||
fill='white'
|
||||
/>
|
||||
<ellipse
|
||||
opacity='0.32'
|
||||
cx='28.5385'
|
||||
cy='91.2594'
|
||||
rx='2.07071'
|
||||
ry='2.06795'
|
||||
fill='white'
|
||||
/>
|
||||
<rect
|
||||
opacity='0.32'
|
||||
x='32.9756'
|
||||
y='90.373'
|
||||
width='27.8067'
|
||||
height='1.77253'
|
||||
rx='0.886265'
|
||||
fill='white'
|
||||
/>
|
||||
<ellipse
|
||||
opacity='0.32'
|
||||
cx='28.5385'
|
||||
cy='77.0789'
|
||||
rx='2.07071'
|
||||
ry='2.06795'
|
||||
fill='white'
|
||||
/>
|
||||
<rect
|
||||
opacity='0.32'
|
||||
x='32.9756'
|
||||
y='76.1926'
|
||||
width='18.3406'
|
||||
height='1.77253'
|
||||
rx='0.886265'
|
||||
fill='white'
|
||||
/>
|
||||
<ellipse
|
||||
cx='74.3896'
|
||||
cy='34.8336'
|
||||
rx='3.54979'
|
||||
ry='3.54506'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='81.4893'
|
||||
y='31.8794'
|
||||
width='20.1155'
|
||||
height='1.77253'
|
||||
rx='0.886265'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='81.4893'
|
||||
y='37.197'
|
||||
width='60.9381'
|
||||
height='1.18169'
|
||||
rx='0.590843'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='81.4893'
|
||||
y='41.9238'
|
||||
width='68.6293'
|
||||
height='1.18169'
|
||||
rx='0.590843'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<ellipse
|
||||
cx='74.3896'
|
||||
cy='59.0582'
|
||||
rx='3.54979'
|
||||
ry='3.54506'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='81.4893'
|
||||
y='56.104'
|
||||
width='20.1155'
|
||||
height='1.77253'
|
||||
rx='0.886265'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='81.4893'
|
||||
y='61.4216'
|
||||
width='60.9381'
|
||||
height='1.18169'
|
||||
rx='0.590843'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='81.4893'
|
||||
y='66.1484'
|
||||
width='68.6293'
|
||||
height='1.18169'
|
||||
rx='0.590843'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<ellipse
|
||||
cx='74.3896'
|
||||
cy='81.5104'
|
||||
rx='3.54979'
|
||||
ry='3.54506'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='81.4893'
|
||||
y='78.5562'
|
||||
width='20.1155'
|
||||
height='1.77253'
|
||||
rx='0.886265'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='81.4893'
|
||||
y='83.8738'
|
||||
width='60.9381'
|
||||
height='1.18169'
|
||||
rx='0.590843'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='81.4893'
|
||||
y='88.6006'
|
||||
width='68.6293'
|
||||
height='1.18169'
|
||||
rx='0.590843'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<ellipse
|
||||
cx='92.73'
|
||||
cy='17.6993'
|
||||
rx='1.77489'
|
||||
ry='1.77253'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<ellipse
|
||||
cx='124.086'
|
||||
cy='17.6993'
|
||||
rx='1.77489'
|
||||
ry='1.77253'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<ellipse
|
||||
cx='154.26'
|
||||
cy='17.6993'
|
||||
rx='1.77489'
|
||||
ry='1.77253'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<ellipse
|
||||
cx='159.584'
|
||||
cy='17.6993'
|
||||
rx='1.77489'
|
||||
ry='1.77253'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<ellipse
|
||||
cx='97.4634'
|
||||
cy='17.6993'
|
||||
rx='1.77489'
|
||||
ry='1.77253'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='70.8398'
|
||||
y='17.1084'
|
||||
width='18.3406'
|
||||
height='1.18169'
|
||||
rx='0.590843'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='127.637'
|
||||
y='16.5176'
|
||||
width='23.0736'
|
||||
height='2.36337'
|
||||
rx='1.18169'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d='M95.6878 2.92798C96.1559 2.92798 96.6134 3.06659 97.0026 3.32629C97.3918 3.58598 97.6951 3.95508 97.8742 4.38693C98.0533 4.81878 98.1002 5.29397 98.0089 5.75242C97.9175 6.21087 97.6922 6.63199 97.3612 6.96251C97.0303 7.29303 96.6086 7.51813 96.1495 7.60932C95.6905 7.70051 95.2146 7.65369 94.7822 7.47481C94.3497 7.29593 93.9801 6.99303 93.7201 6.60438C93.4601 6.21572 93.3213 5.75878 93.3213 5.29135C93.3213 4.66455 93.5707 4.06341 94.0145 3.62019C94.4583 3.17697 95.0602 2.92798 95.6878 2.92798Z'
|
||||
fill='#989DAE'
|
||||
/>
|
||||
<path
|
||||
d='M109.322 138.231H81.4444C80.4823 138.231 77.9395 138.231 77.9395 134.686H112.846C112.846 138.231 110.228 138.231 109.322 138.231Z'
|
||||
fill='#3F4350'
|
||||
/>
|
||||
</g>
|
||||
<g clipPath='url(#clip2_754_57919)'>
|
||||
<rect
|
||||
x='184.015'
|
||||
y='147.833'
|
||||
width='14.3123'
|
||||
height='2.45026'
|
||||
rx='1.22513'
|
||||
fill='#8D93A5'
|
||||
/>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M219.65 155.56C218.049 157.196 215.767 158.45 213.264 158.45H169.486C166.983 158.45 164.701 157.196 163.1 155.56C161.496 153.92 160.297 151.613 160.297 149.117V47.721C160.297 45.2239 161.494 42.9173 163.098 41.2769C164.698 39.6401 166.979 38.3875 169.482 38.3875H213.264C215.766 38.3875 218.048 39.6398 219.65 41.2762C221.254 42.9162 222.453 45.223 222.453 47.721V149.117C222.453 151.613 221.254 153.92 219.65 155.56ZM213.264 156C216.632 156 219.999 152.554 219.999 149.117V47.721C219.999 44.2793 216.632 40.8377 213.264 40.8377H169.482C166.114 40.8377 162.75 44.2793 162.75 47.721V149.117C162.75 152.554 166.118 156 169.486 156H213.264Z'
|
||||
fill='#363A45'
|
||||
/>
|
||||
<path
|
||||
d='M220 149.117C220 152.554 216.632 156 213.265 156H169.486C166.119 156 162.751 152.554 162.751 149.117V47.7209C162.751 44.2793 166.115 40.8376 169.482 40.8376H213.265C216.632 40.8376 220 44.2793 220 47.7209V149.117Z'
|
||||
fill='#3F4350'
|
||||
/>
|
||||
<path
|
||||
d='M196.283 46.7579C196.283 47.0974 196.068 47.3719 195.796 47.3719H186.959C186.691 47.3719 186.469 47.0889 186.469 46.7579C186.469 46.4268 186.691 46.1467 186.959 46.1467H195.781C196.053 46.1467 196.283 46.4212 196.283 46.7579Z'
|
||||
fill='#8D93A5'
|
||||
/>
|
||||
<path
|
||||
d='M217.546 53.4976H165.204V143.749H217.546V53.4976Z'
|
||||
fill='white'
|
||||
/>
|
||||
<ellipse
|
||||
cx='171.516'
|
||||
cy='71.2071'
|
||||
rx='3.53454'
|
||||
ry='3.52983'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<ellipse
|
||||
cx='171.516'
|
||||
cy='92.386'
|
||||
rx='3.53454'
|
||||
ry='3.52983'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<ellipse
|
||||
cx='171.516'
|
||||
cy='113.565'
|
||||
rx='3.53454'
|
||||
ry='3.52983'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='176.465'
|
||||
y='69.0891'
|
||||
width='24.0349'
|
||||
height='1.41193'
|
||||
rx='0.705966'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='176.465'
|
||||
y='90.2681'
|
||||
width='24.0349'
|
||||
height='1.41193'
|
||||
rx='0.705967'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='176.465'
|
||||
y='111.447'
|
||||
width='24.0349'
|
||||
height='1.41193'
|
||||
rx='0.705967'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='176.465'
|
||||
y='77.5608'
|
||||
width='33.9316'
|
||||
height='1.41193'
|
||||
rx='0.705966'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='176.465'
|
||||
y='98.7397'
|
||||
width='33.9316'
|
||||
height='1.41193'
|
||||
rx='0.705965'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='176.465'
|
||||
y='119.919'
|
||||
width='33.9316'
|
||||
height='1.41193'
|
||||
rx='0.705963'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='176.465'
|
||||
y='81.7966'
|
||||
width='16.9658'
|
||||
height='1.41193'
|
||||
rx='0.705966'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='176.465'
|
||||
y='102.976'
|
||||
width='16.9658'
|
||||
height='1.41193'
|
||||
rx='0.705965'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='176.465'
|
||||
y='124.155'
|
||||
width='16.9658'
|
||||
height='1.41193'
|
||||
rx='0.705967'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='176.465'
|
||||
y='73.325'
|
||||
width='36.7592'
|
||||
height='1.41193'
|
||||
rx='0.705966'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='176.465'
|
||||
y='94.5039'
|
||||
width='36.7592'
|
||||
height='1.41193'
|
||||
rx='0.705965'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='176.465'
|
||||
y='115.683'
|
||||
width='36.7592'
|
||||
height='1.41193'
|
||||
rx='0.705967'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.24'
|
||||
/>
|
||||
<rect
|
||||
x='165.204'
|
||||
y='53.4976'
|
||||
width='52.342'
|
||||
height='9.80105'
|
||||
fill='#1E325C'
|
||||
/>
|
||||
<rect
|
||||
opacity='0.4'
|
||||
x='173.791'
|
||||
y='57.9897'
|
||||
width='17.1747'
|
||||
height='1.22513'
|
||||
rx='0.612565'
|
||||
fill='white'
|
||||
/>
|
||||
<rect
|
||||
opacity='0.4'
|
||||
x='210.595'
|
||||
y='56.3562'
|
||||
width='4.08922'
|
||||
height='4.08377'
|
||||
rx='2'
|
||||
fill='white'
|
||||
/>
|
||||
<rect
|
||||
opacity='0.4'
|
||||
x='168.066'
|
||||
y='56.3562'
|
||||
width='4.08922'
|
||||
height='4.08377'
|
||||
rx='2'
|
||||
fill='white'
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id='clip0_754_57919'>
|
||||
<rect
|
||||
width='179'
|
||||
height='142'
|
||||
fill='white'
|
||||
/>
|
||||
</clipPath>
|
||||
<clipPath id='clip1_754_57919'>
|
||||
<rect
|
||||
width='146.725'
|
||||
height='94.5349'
|
||||
fill='white'
|
||||
transform='translate(22.3262 10.0183)'
|
||||
/>
|
||||
</clipPath>
|
||||
<clipPath id='clip2_754_57919'>
|
||||
<rect
|
||||
width='63'
|
||||
height='121'
|
||||
fill='white'
|
||||
transform='translate(160 38)'
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
);
|
||||
|
||||
export default AirgappedTrialActivationConfirmSvg;
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
type SvgProps = {
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
const Svg = (props: SvgProps) => (
|
||||
<svg
|
||||
width={props.width?.toString() || '170'}
|
||||
height={props.height?.toString() || '129'}
|
||||
viewBox='0 0 170 129'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<path
|
||||
d='M23.6258 81.7032H134.554C135.371 81.6915 136.149 81.3515 136.719 80.7574C137.29 80.1633 137.605 79.3636 137.597 78.5334V3.15128C137.605 2.32105 137.29 1.52137 136.719 0.927285C136.149 0.333195 135.371 -0.00690192 134.554 -0.0185547H23.6258C22.81 -0.00574534 22.0321 0.334704 21.4621 0.928539C20.8921 1.52237 20.5761 2.32139 20.5831 3.15128V78.5378C20.5773 79.3669 20.8938 80.1648 21.4637 80.7577C22.0336 81.3505 22.8107 81.6904 23.6258 81.7032Z'
|
||||
fill='#3F4350'
|
||||
/>
|
||||
<path
|
||||
d='M11.3887 92.5439C11.3887 96.2965 14.4453 100.049 18.1524 100.049H140.031C143.525 100.049 146.791 96.3052 146.791 92.5439H11.3887Z'
|
||||
fill='#767D93'
|
||||
/>
|
||||
<path
|
||||
d='M135.508 81.7031H22.6718L11.3887 92.5438H146.791L135.508 81.7031Z'
|
||||
fill='#D1D4DB'
|
||||
/>
|
||||
<path
|
||||
d='M132.133 82.5371H26.0413L22.2539 87.1235H135.925L132.133 82.5371Z'
|
||||
fill='#AFB3C0'
|
||||
/>
|
||||
<path
|
||||
d='M90.7217 88.7915H67.4624L65.7168 91.2932H92.4629L90.7217 88.7915Z'
|
||||
fill='#24262E'
|
||||
/>
|
||||
<rect
|
||||
width='103.641'
|
||||
height='66.7116'
|
||||
transform='translate(27.2695 7.06982)'
|
||||
fill='white'
|
||||
/>
|
||||
<rect
|
||||
x='27.2695'
|
||||
y='7.06982'
|
||||
width='104'
|
||||
height='67'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.16'
|
||||
/>
|
||||
<path
|
||||
d='M79.0896 2.06641C79.4202 2.06641 79.7434 2.16423 80.0183 2.34748C80.2932 2.53074 80.5075 2.79121 80.634 3.09596C80.7605 3.40071 80.7936 3.73605 80.7291 4.05957C80.6646 4.38309 80.5054 4.68026 80.2717 4.9135C80.0379 5.14675 79.74 5.30559 79.4157 5.36994C79.0915 5.4343 78.7553 5.40125 78.4499 5.27502C78.1444 5.14879 77.8834 4.93504 77.6997 4.66077C77.516 4.38651 77.418 4.06405 77.418 3.7342C77.418 3.29187 77.5941 2.86766 77.9076 2.55489C78.2211 2.24212 78.6463 2.06641 79.0896 2.06641Z'
|
||||
fill='#989DAE'
|
||||
/>
|
||||
<path
|
||||
d='M88.7203 97.5471H69.0285C68.3489 97.5471 66.5527 97.5471 66.5527 95.0454H91.2093C91.2093 97.5471 89.3602 97.5471 88.7203 97.5471Z'
|
||||
fill='#3F4350'
|
||||
/>
|
||||
<path
|
||||
d='M60.1793 63.0923C57.6677 63.0923 56.5297 61.286 57.6504 59.0783L77.9502 19.2945C79.0997 17.0926 80.9101 17.0926 82.0366 19.2945L102.331 59.0783C103.48 61.2803 102.331 63.0923 99.8019 63.0923H60.1793Z'
|
||||
fill='#FFBC1F'
|
||||
/>
|
||||
<path
|
||||
d='M76.8061 34.0084L78.8924 47.9941C78.9125 48.2716 79.0372 48.5312 79.2414 48.7207C79.4456 48.9103 79.7141 49.0156 79.993 49.0156C80.2719 49.0156 80.5404 48.9103 80.7446 48.7207C80.9488 48.5312 81.0736 48.2716 81.0937 47.9941L83.18 34.0084C83.5593 28.5552 76.421 28.5552 76.8061 34.0084Z'
|
||||
fill='#2D3039'
|
||||
/>
|
||||
<path
|
||||
d='M79.9903 50.523C80.6221 50.5241 81.2393 50.712 81.7641 51.063C82.2888 51.4141 82.6976 51.9124 82.9385 52.4951C83.1795 53.0777 83.242 53.7186 83.118 54.3367C82.9941 54.9547 82.6893 55.5223 82.2421 55.9676C81.795 56.4129 81.2256 56.716 80.6059 56.8385C79.9862 56.9611 79.344 56.8976 78.7604 56.6561C78.1768 56.4147 77.6781 56.006 77.3272 55.4818C76.9763 54.9577 76.7891 54.3415 76.7891 53.7112C76.7891 53.292 76.8718 52.8769 77.0328 52.4898C77.1937 52.1026 77.4297 51.7509 77.727 51.4547C78.0244 51.1586 78.3773 50.9239 78.7657 50.764C79.1541 50.6041 79.5702 50.5222 79.9903 50.523Z'
|
||||
fill='#2D3039'
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default Svg;
|
||||
@@ -132,7 +132,7 @@ const FeatureRestrictedModal = ({
|
||||
trialBtn = (
|
||||
<CloudStartTrialButton
|
||||
extraClass='button-trial'
|
||||
message={formatMessage({id: 'trial_btn.free.tryFreeFor30Days', defaultMessage: 'Try free for 30 days'})}
|
||||
message={formatMessage({id: 'trial_btn.free.tryFreeFor30Days', defaultMessage: 'Start trial'})}
|
||||
telemetryId={'start_cloud_trial_after_team_creation_restricted'}
|
||||
onClick={dismissAction}
|
||||
/>
|
||||
@@ -140,7 +140,8 @@ const FeatureRestrictedModal = ({
|
||||
} else {
|
||||
trialBtn = (
|
||||
<StartTrialBtn
|
||||
message={formatMessage({id: 'trial_btn.free.tryFreeFor30Days', defaultMessage: 'Try free for 30 days'})}
|
||||
message={formatMessage({id: 'trial_btn.free.tryFreeFor30Days', defaultMessage: 'Start trial'})}
|
||||
onClick={dismissAction}
|
||||
telemetryId='start_self_hosted_trial_after_team_creation_restricted'
|
||||
btnClass='btn btn-primary'
|
||||
renderAsButton={true}
|
||||
|
||||
@@ -69,7 +69,7 @@ const LearnMoreTrialModal = (
|
||||
|
||||
// no need to check if is cloud trial or if it have had prev cloud trial because the button that show this modal takes care of that
|
||||
if (isCloud) {
|
||||
startTrialBtnMsg = formatMessage({id: 'trial_btn.free.tryFreeFor30Days', defaultMessage: 'Try free for 30 days'});
|
||||
startTrialBtnMsg = formatMessage({id: 'trial_btn.free.tryFreeFor30Days', defaultMessage: 'Start trial'});
|
||||
startTrialBtn = (
|
||||
<CloudStartTrialButton
|
||||
message={startTrialBtnMsg}
|
||||
|
||||
@@ -12,12 +12,8 @@ import {act} from 'react-dom/test-utils';
|
||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
||||
import mockStore from 'tests/test_store';
|
||||
|
||||
import {trackEvent} from 'actions/telemetry_actions.jsx';
|
||||
|
||||
import StartTrialBtn from 'components/learn_more_trial_modal/start_trial_btn';
|
||||
|
||||
import {TELEMETRY_CATEGORIES} from 'utils/constants';
|
||||
|
||||
jest.mock('actions/telemetry_actions.jsx', () => {
|
||||
const original = jest.requireActual('actions/telemetry_actions.jsx');
|
||||
return {
|
||||
@@ -116,8 +112,6 @@ describe('components/learn_more_trial_modal/start_trial_btn', () => {
|
||||
});
|
||||
|
||||
expect(mockOnClick).toHaveBeenCalled();
|
||||
|
||||
expect(trackEvent).toHaveBeenCalledWith(TELEMETRY_CATEGORIES.SELF_HOSTED_START_TRIAL_MODAL, 'test_telemetry_id');
|
||||
});
|
||||
|
||||
test('should handle on click when rendered as button', async () => {
|
||||
@@ -143,33 +137,31 @@ describe('components/learn_more_trial_modal/start_trial_btn', () => {
|
||||
});
|
||||
|
||||
expect(mockOnClick).toHaveBeenCalled();
|
||||
|
||||
expect(trackEvent).toHaveBeenCalledWith(TELEMETRY_CATEGORIES.SELF_HOSTED_START_TRIAL_MODAL, 'test_telemetry_id');
|
||||
});
|
||||
|
||||
test('does not show success for embargoed countries', async () => {
|
||||
const mockOnClick = jest.fn();
|
||||
// test('does not show success for embargoed countries', async () => {
|
||||
// const mockOnClick = jest.fn();
|
||||
|
||||
let wrapper: ReactWrapper<any>;
|
||||
const clonedState = JSON.parse(JSON.stringify(state));
|
||||
clonedState.entities.admin.analytics.TOTAL_USERS = 451;
|
||||
// let wrapper: ReactWrapper<any>;
|
||||
// const clonedState = JSON.parse(JSON.stringify(state));
|
||||
// clonedState.entities.admin.analytics.TOTAL_USERS = 451;
|
||||
|
||||
// Mount the component
|
||||
await act(async () => {
|
||||
wrapper = mountWithIntl(
|
||||
<Provider store={mockStore(clonedState)}>
|
||||
<StartTrialBtn
|
||||
{...props}
|
||||
onClick={mockOnClick}
|
||||
/>
|
||||
</Provider>,
|
||||
);
|
||||
});
|
||||
// // Mount the component
|
||||
// await act(async () => {
|
||||
// wrapper = mountWithIntl(
|
||||
// <Provider store={mockStore(clonedState)}>
|
||||
// <StartTrialBtn
|
||||
// {...props}
|
||||
// onClick={mockOnClick}
|
||||
// />
|
||||
// </Provider>,
|
||||
// );
|
||||
// });
|
||||
|
||||
await act(async () => {
|
||||
wrapper.find('.start-trial-btn').simulate('click');
|
||||
});
|
||||
// await act(async () => {
|
||||
// wrapper.find('.start-trial-btn').simulate('click');
|
||||
// });
|
||||
|
||||
expect(mockOnClick).not.toHaveBeenCalled();
|
||||
});
|
||||
// expect(mockOnClick).not.toHaveBeenCalled();
|
||||
// });
|
||||
});
|
||||
|
||||
@@ -1,27 +1,10 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useState} from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import {useIntl} from 'react-intl';
|
||||
|
||||
import {useDispatch, useSelector} from 'react-redux';
|
||||
|
||||
import {EmbargoedEntityTrialError} from 'components/admin_console/license_settings/trial_banner/trial_banner';
|
||||
|
||||
import {DispatchFunc} from 'mattermost-redux/types/actions';
|
||||
import {getLicenseConfig} from 'mattermost-redux/actions/general';
|
||||
|
||||
import {GlobalState} from 'types/store';
|
||||
|
||||
import {requestTrialLicense} from 'actions/admin_actions';
|
||||
import {trackEvent} from 'actions/telemetry_actions';
|
||||
|
||||
import {openModal} from 'actions/views/modals';
|
||||
|
||||
import TrialBenefitsModal from 'components/trial_benefits_modal/trial_benefits_modal';
|
||||
|
||||
import {ModalIdentifiers, TELEMETRY_CATEGORIES} from 'utils/constants';
|
||||
import useOpenStartTrialFormModal from 'components/common/hooks/useOpenStartTrialFormModal';
|
||||
|
||||
import './start_trial_btn.scss';
|
||||
|
||||
@@ -36,116 +19,31 @@ export type StartTrialBtnProps = {
|
||||
trackingPage?: string;
|
||||
};
|
||||
|
||||
enum TrialLoadStatus {
|
||||
NotStarted = 'NOT_STARTED',
|
||||
Started = 'STARTED',
|
||||
Success = 'SUCCESS',
|
||||
Failed = 'FAILED',
|
||||
Embargoed = 'EMBARGOED',
|
||||
}
|
||||
|
||||
const StartTrialBtn = ({
|
||||
message,
|
||||
btnClass,
|
||||
telemetryId,
|
||||
onClick,
|
||||
handleEmbargoError,
|
||||
disabled = false,
|
||||
renderAsButton = false,
|
||||
trackingPage = 'licensing',
|
||||
}: StartTrialBtnProps) => {
|
||||
const {formatMessage} = useIntl();
|
||||
const dispatch = useDispatch<DispatchFunc>();
|
||||
const stats = useSelector((state: GlobalState) => state.entities.admin.analytics);
|
||||
|
||||
const [status, setLoadStatus] = useState(TrialLoadStatus.NotStarted);
|
||||
|
||||
const requestLicense = async (): Promise<TrialLoadStatus> => {
|
||||
setLoadStatus(TrialLoadStatus.Started);
|
||||
let users = 0;
|
||||
if (stats && (typeof stats.TOTAL_USERS === 'number')) {
|
||||
users = stats.TOTAL_USERS;
|
||||
}
|
||||
const requestedUsers = Math.max(users, 30);
|
||||
const {error, data} = await dispatch(requestTrialLicense(requestedUsers, true, true, trackingPage));
|
||||
if (error) {
|
||||
if (typeof data?.status !== 'undefined' && data.status === 451) {
|
||||
setLoadStatus(TrialLoadStatus.Embargoed);
|
||||
if (typeof handleEmbargoError === 'function') {
|
||||
handleEmbargoError();
|
||||
}
|
||||
return TrialLoadStatus.Embargoed;
|
||||
}
|
||||
setLoadStatus(TrialLoadStatus.Failed);
|
||||
return TrialLoadStatus.Failed;
|
||||
}
|
||||
|
||||
await dispatch(getLicenseConfig());
|
||||
setLoadStatus(TrialLoadStatus.Success);
|
||||
return TrialLoadStatus.Success;
|
||||
};
|
||||
|
||||
const openTrialBenefitsModal = async (status: TrialLoadStatus) => {
|
||||
// Only open the benefits modal if the trial request succeeded
|
||||
if (status !== TrialLoadStatus.Success) {
|
||||
return;
|
||||
}
|
||||
await dispatch(openModal({
|
||||
modalId: ModalIdentifiers.TRIAL_BENEFITS_MODAL,
|
||||
dialogType: TrialBenefitsModal,
|
||||
dialogProps: {trialJustStarted: true},
|
||||
}));
|
||||
};
|
||||
|
||||
const btnText = (status: TrialLoadStatus): string => {
|
||||
switch (status) {
|
||||
case TrialLoadStatus.Started:
|
||||
return formatMessage({id: 'start_trial.modal.gettingTrial', defaultMessage: 'Getting Trial...'});
|
||||
case TrialLoadStatus.Success:
|
||||
return formatMessage({id: 'start_trial.modal.loaded', defaultMessage: 'Loaded!'});
|
||||
case TrialLoadStatus.Failed:
|
||||
return formatMessage({id: 'start_trial.modal.failed', defaultMessage: 'Failed'});
|
||||
case TrialLoadStatus.Embargoed:
|
||||
return formatMessage({id: 'admin.license.trial-request.embargoed'});
|
||||
default:
|
||||
return message;
|
||||
}
|
||||
};
|
||||
const openTrialForm = useOpenStartTrialFormModal();
|
||||
const startTrial = async () => {
|
||||
// reading status from here instead of normal flow because
|
||||
// by the time the function needs the updated value from requestLicense,
|
||||
// it will be too late to wait for the render cycle to happen again
|
||||
// to close over the updated value
|
||||
const updatedStatus = await requestLicense();
|
||||
|
||||
if (updatedStatus !== TrialLoadStatus.Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
trackEvent(
|
||||
TELEMETRY_CATEGORIES.SELF_HOSTED_START_TRIAL_MODAL,
|
||||
telemetryId,
|
||||
);
|
||||
openTrialForm();
|
||||
|
||||
// on click will execute whatever action is sent from the invoking place, if nothing is sent, open the trial benefits modal
|
||||
if (onClick) {
|
||||
onClick();
|
||||
return;
|
||||
}
|
||||
|
||||
await openTrialBenefitsModal(updatedStatus);
|
||||
};
|
||||
|
||||
if (status === TrialLoadStatus.Embargoed) {
|
||||
return (
|
||||
<div className='StartTrialBtn embargoed'>
|
||||
<EmbargoedEntityTrialError/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const id = 'start_trial_btn';
|
||||
|
||||
const btnText = formatMessage({id: 'admin.ldap_feature_discovery.call_to_action.primary', defaultMessage: 'Start trial'});
|
||||
|
||||
return renderAsButton ? (
|
||||
<button
|
||||
id={id}
|
||||
@@ -153,7 +51,7 @@ const StartTrialBtn = ({
|
||||
onClick={startTrial}
|
||||
disabled={disabled}
|
||||
>
|
||||
{btnText(status)}
|
||||
{btnText}
|
||||
</button>
|
||||
) : (
|
||||
<a
|
||||
@@ -161,7 +59,7 @@ const StartTrialBtn = ({
|
||||
className='StartTrialBtn start-trial-btn'
|
||||
onClick={startTrial}
|
||||
>
|
||||
{btnText(status)}
|
||||
{btnText}
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -199,7 +199,7 @@ const Completed = (props: Props): JSX.Element => {
|
||||
</span>
|
||||
{isCloud ? (
|
||||
<CloudStartTrialButton
|
||||
message={formatMessage({id: 'trial_btn.free.tryFreeFor30Days', defaultMessage: 'Try free for 30 days'})}
|
||||
message={formatMessage({id: 'trial_btn.free.tryFreeFor30Days', defaultMessage: 'Start trial'})}
|
||||
telemetryId={'start_cloud_trial_after_completing_steps'}
|
||||
extraClass={'btn btn-primary'}
|
||||
afterTrialRequest={dismissAction}
|
||||
|
||||
@@ -0,0 +1,392 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`components/start_trial_form_modal/start_trial_form_modal should match snapshot 1`] = `
|
||||
Object {
|
||||
"asFragment": [Function],
|
||||
"baseElement": <body
|
||||
class="modal-open"
|
||||
style="padding-right: 0px;"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<div
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="fade modal-backdrop in"
|
||||
/>
|
||||
<div
|
||||
class="fade StartTrialFormModal in modal"
|
||||
id="StartTrialFormModal"
|
||||
role="dialog"
|
||||
style="display: block; padding-right: 0px;"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="a11y__modal modal-dialog"
|
||||
>
|
||||
<div
|
||||
class="modal-content"
|
||||
role="document"
|
||||
>
|
||||
<div
|
||||
class="modal-header"
|
||||
>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="close"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
class="title"
|
||||
>
|
||||
Start Trial
|
||||
</div>
|
||||
<div
|
||||
class="description"
|
||||
>
|
||||
Just a few quick items to help us tailor your trial experience.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="modal-body"
|
||||
>
|
||||
<div
|
||||
class="Input_container"
|
||||
>
|
||||
<fieldset
|
||||
class="Input_fieldset name_input"
|
||||
>
|
||||
<legend
|
||||
class="Input_legend"
|
||||
/>
|
||||
<div
|
||||
class="Input_wrapper"
|
||||
>
|
||||
<input
|
||||
class="Input form-control large"
|
||||
id="input_name"
|
||||
name="name"
|
||||
placeholder="Name"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div
|
||||
class="Input_container"
|
||||
>
|
||||
<fieldset
|
||||
class="Input_fieldset email_input Input_fieldset___error Input_fieldset___legend"
|
||||
>
|
||||
<legend
|
||||
class="Input_legend Input_legend___focus"
|
||||
>
|
||||
Business Email
|
||||
</legend>
|
||||
<div
|
||||
class="Input_wrapper"
|
||||
>
|
||||
<input
|
||||
class="Input form-control large Input__focus"
|
||||
id="input_email"
|
||||
name="email"
|
||||
placeholder="Business Email"
|
||||
type="text"
|
||||
value="test@mattermost.com"
|
||||
/>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div
|
||||
class="Input___customMessage Input___error"
|
||||
>
|
||||
<i
|
||||
class="icon error icon-alert-circle-outline"
|
||||
/>
|
||||
<span>
|
||||
Please enter a valid business email address.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="Input_container"
|
||||
>
|
||||
<fieldset
|
||||
class="Input_fieldset company_name_input"
|
||||
>
|
||||
<legend
|
||||
class="Input_legend"
|
||||
/>
|
||||
<div
|
||||
class="Input_wrapper"
|
||||
>
|
||||
<input
|
||||
class="Input form-control large"
|
||||
id="input_company_name"
|
||||
name="company_name"
|
||||
placeholder="Company Name"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div
|
||||
class="DropdownInput Input_container"
|
||||
data-testid=""
|
||||
>
|
||||
<fieldset
|
||||
class="Input_fieldset company_size_dropdown"
|
||||
>
|
||||
<legend
|
||||
class="Input_legend"
|
||||
/>
|
||||
<div
|
||||
class="Input_wrapper"
|
||||
>
|
||||
<div
|
||||
class="Input company_size_dropdown css-2b097c-container"
|
||||
id="DropdownInput_company_size_dropdown"
|
||||
>
|
||||
<div
|
||||
class="DropdownInput__controlContainer"
|
||||
>
|
||||
<div
|
||||
class="DropDown__control css-1nr98fh-control"
|
||||
>
|
||||
<div
|
||||
class="DropDown__value-container css-1hwfws3"
|
||||
>
|
||||
<div
|
||||
class="DropDown__placeholder css-1wa3eu0-placeholder"
|
||||
>
|
||||
Company Size
|
||||
</div>
|
||||
<div
|
||||
class="css-1ed09z3-Input"
|
||||
>
|
||||
<div
|
||||
class="DropDown__input"
|
||||
style="display: inline-block;"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
autocapitalize="none"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
id="react-select-2-input"
|
||||
spellcheck="false"
|
||||
style="box-sizing: content-box; width: 2px; border: 0px; opacity: 1; outline: 0; padding: 0px;"
|
||||
tabindex="0"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="DropdownInput__indicatorsContainer"
|
||||
>
|
||||
<div
|
||||
class="DropDown__indicators css-1hb7zxy-IndicatorsContainer"
|
||||
>
|
||||
<i
|
||||
class="icon icon-chevron-down"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div
|
||||
class="countries-section"
|
||||
>
|
||||
<div
|
||||
class="DropdownInput Input_container"
|
||||
data-testid=""
|
||||
>
|
||||
<fieldset
|
||||
class="Input_fieldset"
|
||||
>
|
||||
<legend
|
||||
class="Input_legend"
|
||||
/>
|
||||
<div
|
||||
class="Input_wrapper"
|
||||
>
|
||||
<div
|
||||
class="Input css-2b097c-container"
|
||||
id="DropdownInput_country_dropdown"
|
||||
>
|
||||
<div
|
||||
class="DropdownInput__controlContainer"
|
||||
>
|
||||
<div
|
||||
class="DropDown__control css-1nr98fh-control"
|
||||
>
|
||||
<div
|
||||
class="DropDown__value-container css-1hwfws3"
|
||||
>
|
||||
<div
|
||||
class="DropDown__placeholder css-1wa3eu0-placeholder"
|
||||
>
|
||||
Country
|
||||
</div>
|
||||
<div
|
||||
class="css-1ed09z3-Input"
|
||||
>
|
||||
<div
|
||||
class="DropDown__input"
|
||||
style="display: inline-block;"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
autocapitalize="none"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
id="react-select-3-input"
|
||||
spellcheck="false"
|
||||
style="box-sizing: content-box; width: 2px; border: 0px; opacity: 1; outline: 0; padding: 0px;"
|
||||
tabindex="0"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="DropdownInput__indicatorsContainer"
|
||||
>
|
||||
<div
|
||||
class="DropDown__indicators css-1hb7zxy-IndicatorsContainer"
|
||||
>
|
||||
<i
|
||||
class="icon icon-chevron-down"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="disclaimer"
|
||||
>
|
||||
By selecting Start trial, I agree to the
|
||||
<a
|
||||
href="https://mattermost.com/software-evaluation-agreement/?utm_source=mattermost&utm_medium=in-product&utm_content=start_trial_form_modal&uid=user1&sid=test123"
|
||||
location="start_trial_form_modal"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Mattermost Software Evaluation Agreement
|
||||
</a>
|
||||
,
|
||||
<a
|
||||
href="https://mattermost.com/privacy-policy/?utm_source=mattermost&utm_medium=in-product&utm_content=start_trial_form_modal&uid=user1&sid=test123"
|
||||
location="start_trial_form_modal"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Privacy Policy
|
||||
</a>
|
||||
, and receiving product emails.
|
||||
</div>
|
||||
<div
|
||||
class="buttons"
|
||||
>
|
||||
<button
|
||||
class="confirm-btn btn btn-default"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
Start trial
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>,
|
||||
"container": <div
|
||||
aria-hidden="true"
|
||||
/>,
|
||||
"debug": [Function],
|
||||
"findAllByAltText": [Function],
|
||||
"findAllByDisplayValue": [Function],
|
||||
"findAllByLabelText": [Function],
|
||||
"findAllByPlaceholderText": [Function],
|
||||
"findAllByRole": [Function],
|
||||
"findAllByTestId": [Function],
|
||||
"findAllByText": [Function],
|
||||
"findAllByTitle": [Function],
|
||||
"findByAltText": [Function],
|
||||
"findByDisplayValue": [Function],
|
||||
"findByLabelText": [Function],
|
||||
"findByPlaceholderText": [Function],
|
||||
"findByRole": [Function],
|
||||
"findByTestId": [Function],
|
||||
"findByText": [Function],
|
||||
"findByTitle": [Function],
|
||||
"getAllByAltText": [Function],
|
||||
"getAllByDisplayValue": [Function],
|
||||
"getAllByLabelText": [Function],
|
||||
"getAllByPlaceholderText": [Function],
|
||||
"getAllByRole": [Function],
|
||||
"getAllByTestId": [Function],
|
||||
"getAllByText": [Function],
|
||||
"getAllByTitle": [Function],
|
||||
"getByAltText": [Function],
|
||||
"getByDisplayValue": [Function],
|
||||
"getByLabelText": [Function],
|
||||
"getByPlaceholderText": [Function],
|
||||
"getByRole": [Function],
|
||||
"getByTestId": [Function],
|
||||
"getByText": [Function],
|
||||
"getByTitle": [Function],
|
||||
"queryAllByAltText": [Function],
|
||||
"queryAllByDisplayValue": [Function],
|
||||
"queryAllByLabelText": [Function],
|
||||
"queryAllByPlaceholderText": [Function],
|
||||
"queryAllByRole": [Function],
|
||||
"queryAllByTestId": [Function],
|
||||
"queryAllByText": [Function],
|
||||
"queryAllByTitle": [Function],
|
||||
"queryByAltText": [Function],
|
||||
"queryByDisplayValue": [Function],
|
||||
"queryByLabelText": [Function],
|
||||
"queryByPlaceholderText": [Function],
|
||||
"queryByRole": [Function],
|
||||
"queryByTestId": [Function],
|
||||
"queryByText": [Function],
|
||||
"queryByTitle": [Function],
|
||||
"rerender": [Function],
|
||||
"unmount": [Function],
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,117 @@
|
||||
.AirGappedModal {
|
||||
.modal-dialog {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 512px;
|
||||
height: 660px;
|
||||
border: 1 px solid rgba(var(--center-channel-color-rgb), 0.08);
|
||||
margin: auto;
|
||||
border-radius: 12px;
|
||||
transform: translate(-50%, -50%) !important;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
.close {
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus,
|
||||
&:active:focus {
|
||||
background-color: rgba(var(--center-channel-color-rgb), 0.08);
|
||||
color: rgba(var(--center-channel-color-rgb), 0.72);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
top: 26px;
|
||||
right: 26px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 4px;
|
||||
color: rgba(var(--center-channel-color-rgb), 0.56) !important;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-size: 32px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: Metropolis, sans-serif;
|
||||
font-size: 22px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 28px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
display: block;
|
||||
min-height: 48px;
|
||||
padding-top: 26px;
|
||||
padding-bottom: 0;
|
||||
padding-left: 32px;
|
||||
border: 0;
|
||||
background: var(--center-channel-bg) !important;
|
||||
border-radius: 12px;
|
||||
color: var(--center-channel-color);
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
padding-top: 24px;
|
||||
padding-right: 32px;
|
||||
padding-bottom: 26px;
|
||||
padding-left: 32px;
|
||||
|
||||
.description {
|
||||
margin: 0 auto;
|
||||
margin-top: 8px;
|
||||
color: rgb(var(--center-channel-color-rgb));
|
||||
font-family: 'Open Sans';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
margin-top: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
margin-top: 32px;
|
||||
text-align: right;
|
||||
|
||||
.confirm-btn {
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus,
|
||||
&:active:focus {
|
||||
background:
|
||||
linear-gradient(0deg, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0.16)),
|
||||
var(--button-bg);
|
||||
}
|
||||
|
||||
height: 40px;
|
||||
flex: none;
|
||||
padding: 12px 20px;
|
||||
border: none;
|
||||
margin-left: 8px;
|
||||
background: var(--button-bg);
|
||||
border-radius: 4px;
|
||||
color: var(--sys-button-color);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {Button, Modal} from 'react-bootstrap';
|
||||
import {useIntl} from 'react-intl';
|
||||
import AirgappedTrialActivationConfirmSvg from 'components/common/svg_images_components/airgapped_trial_activation_confirm_svg';
|
||||
|
||||
import './air_gapped_modal.scss';
|
||||
import ExternalLink from 'components/external_link';
|
||||
|
||||
type Props = {
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
function AirGappedModal({onClose}: Props) {
|
||||
const {formatMessage} = useIntl();
|
||||
const airGappedLink = (
|
||||
<ExternalLink
|
||||
location='start_trial_air_gapped_modal'
|
||||
href='https://mattermost.com/trial/'
|
||||
>
|
||||
{'https://mattermost.com/trial/'}
|
||||
</ExternalLink>
|
||||
);
|
||||
return (
|
||||
<Modal
|
||||
className={'AirGappedModal'}
|
||||
dialogClassName={'AirGappedModal__dialog'}
|
||||
show={true}
|
||||
id='airGappedModal'
|
||||
role='dialog'
|
||||
onHide={() => onClose?.()}
|
||||
>
|
||||
<Modal.Header closeButton={true}>
|
||||
<div className='title'>
|
||||
{formatMessage({id: 'air_gapped_modal.title', defaultMessage: 'Request a trial key'})}
|
||||
</div>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<div className='body'>
|
||||
<div className='description'>
|
||||
{
|
||||
formatMessage(
|
||||
{
|
||||
id: 'air_gapped_modal.description',
|
||||
defaultMessage: 'To start your trial, please visit {link} and request a trial key.',
|
||||
},
|
||||
{
|
||||
link: airGappedLink,
|
||||
},
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div className='icon'>
|
||||
<AirgappedTrialActivationConfirmSvg
|
||||
width={256}
|
||||
height={200}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='buttons'>
|
||||
<Button
|
||||
className='confirm-btn'
|
||||
onClick={() => onClose?.()}
|
||||
>
|
||||
{formatMessage({id: 'air_gapped_modal.close', defaultMessage: 'Close'})}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default AirGappedModal;
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import {useDispatch} from 'react-redux';
|
||||
|
||||
import {closeModal} from 'actions/views/modals';
|
||||
import {ModalIdentifiers} from 'utils/constants';
|
||||
import LaptopAlertSvg from 'components/common/svg_images_components/laptop_with_warning_symbol_svg';
|
||||
|
||||
import ResultModal from 'components/admin_console/billing/delete_workspace/result_modal';
|
||||
|
||||
type Props = {
|
||||
onTryAgain?: () => void;
|
||||
title?: JSX.Element;
|
||||
subtitle?: JSX.Element;
|
||||
buttonText?: JSX.Element;
|
||||
}
|
||||
|
||||
export default function StartTrialFormModalResult(props: Props) {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleButtonClick = () => {
|
||||
props.onTryAgain?.();
|
||||
dispatch(closeModal(ModalIdentifiers.START_TRIAL_FORM_MODAL_RESULT));
|
||||
};
|
||||
|
||||
const title = props.title || (
|
||||
<FormattedMessage
|
||||
defaultMessage={'Please try again'}
|
||||
id={'start_trial_form_modal.failureModal.title'}
|
||||
/>
|
||||
);
|
||||
|
||||
const subtitle = (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id={'start_trial_form_modal.failureModal.subtitle'}
|
||||
defaultMessage={'There was an issue processing your trial request.'}
|
||||
/>
|
||||
<br/>
|
||||
<FormattedMessage
|
||||
id={'start_trial_form_modal.failureModal.subtitle2'}
|
||||
defaultMessage={'Please try again or contact support.'}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
const buttonText = props.buttonText || (
|
||||
<FormattedMessage
|
||||
id='admin.billing.deleteWorkspace.failureModal.buttonText'
|
||||
defaultMessage={'Try Again'}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<ResultModal
|
||||
primaryButtonText={buttonText}
|
||||
primaryButtonHandler={handleButtonClick}
|
||||
onHide={handleButtonClick}
|
||||
identifier={ModalIdentifiers.START_TRIAL_FORM_MODAL_RESULT}
|
||||
subtitle={subtitle}
|
||||
title={title}
|
||||
ignoreExit={false}
|
||||
type='small'
|
||||
resultType='failure'
|
||||
icon={
|
||||
<LaptopAlertSvg
|
||||
width={135}
|
||||
height={100}
|
||||
/>
|
||||
}
|
||||
contactSupportButtonVisible={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
361
webapp/channels/src/components/start_trial_form_modal/index.tsx
Обычный файл
361
webapp/channels/src/components/start_trial_form_modal/index.tsx
Обычный файл
@@ -0,0 +1,361 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {useSelector, useDispatch} from 'react-redux';
|
||||
import {Modal, Button} from 'react-bootstrap';
|
||||
import {FormattedMessage, useIntl} from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {isModalOpen} from 'selectors/views/modals';
|
||||
import {GlobalState} from 'types/store';
|
||||
import {closeModal, openModal} from 'actions/views/modals';
|
||||
import {requestTrialLicense} from 'actions/admin_actions';
|
||||
import {validateBusinessEmail} from 'actions/cloud';
|
||||
|
||||
import {getLicenseConfig} from 'mattermost-redux/actions/general';
|
||||
import {DispatchFunc} from 'mattermost-redux/types/actions';
|
||||
import {getCurrentUser} from 'mattermost-redux/selectors/entities/common';
|
||||
|
||||
import {makeAsyncComponent} from 'components/async_load';
|
||||
import useGetTotalUsersNoBots from 'components/common/hooks/useGetTotalUsersNoBots';
|
||||
import {COUNTRIES} from 'utils/countries';
|
||||
|
||||
import {LicenseLinks, ModalIdentifiers, TELEMETRY_CATEGORIES} from 'utils/constants';
|
||||
|
||||
import Input, {SIZE, CustomMessageInputType} from 'components/widgets/inputs/input/input';
|
||||
import DropdownInput from 'components/dropdown_input';
|
||||
import StartTrialFormModalResult from './failure_modal';
|
||||
import ExternalLink from 'components/external_link';
|
||||
import {trackEvent} from 'actions/telemetry_actions';
|
||||
|
||||
import './start_trial_form_modal.scss';
|
||||
import useCWSAvailabilityCheck from 'components/common/hooks/useCWSAvailabilityCheck';
|
||||
import AirGappedModal from './air_gapped_modal';
|
||||
|
||||
// TODO: Handle embargoed entities explicitly https://mattermost.atlassian.net/browse/MM-51470
|
||||
|
||||
const TrialBenefitsModal = makeAsyncComponent('TrialBenefitsModal', React.lazy(() => import('components/trial_benefits_modal/trial_benefits_modal')));
|
||||
|
||||
enum TrialLoadStatus {
|
||||
NotStarted = 'NOT_STARTED',
|
||||
Started = 'STARTED',
|
||||
Success = 'SUCCESS',
|
||||
Failed = 'FAILED'
|
||||
}
|
||||
|
||||
export enum OrgSize {
|
||||
ONE_TO_50 = '1-50',
|
||||
FIFTY_TO_100 = '51-100',
|
||||
ONE_HUNDRED_TO_500 = '101-500',
|
||||
}
|
||||
|
||||
type Props = {
|
||||
onClose?: () => void;
|
||||
page?: string;
|
||||
}
|
||||
|
||||
function StartTrialFormModal(props: Props): JSX.Element | null {
|
||||
const [status, setLoadStatus] = useState(TrialLoadStatus.NotStarted);
|
||||
const dispatch = useDispatch<DispatchFunc>();
|
||||
const currentUser = useSelector(getCurrentUser);
|
||||
const [name, setName] = useState('');
|
||||
const [email, setEmail] = useState(currentUser.email);
|
||||
const [companyName, setCompanyName] = useState('');
|
||||
const [orgSize, setOrgSize] = useState<OrgSize | undefined>();
|
||||
const [country, setCountry] = useState('');
|
||||
const [businessEmailError, setBusinessEmailError] = useState<CustomMessageInputType | undefined>(undefined);
|
||||
const {formatMessage} = useIntl();
|
||||
const canReachCWS = useCWSAvailabilityCheck();
|
||||
const show = useSelector((state: GlobalState) => isModalOpen(state, ModalIdentifiers.START_TRIAL_FORM_MODAL));
|
||||
const totalUsers = useGetTotalUsersNoBots(true) || 0;
|
||||
const [didOnce, setDidOnce] = useState(false);
|
||||
|
||||
const handleValidateBusinessEmail = async (email: string) => {
|
||||
setDidOnce(true);
|
||||
if (!email) {
|
||||
setBusinessEmailError(undefined);
|
||||
return;
|
||||
}
|
||||
const isBusinessEmail = await validateBusinessEmail(email)();
|
||||
|
||||
if (isBusinessEmail) {
|
||||
setBusinessEmailError(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
setBusinessEmailError({
|
||||
type: 'error',
|
||||
value: formatMessage({id: 'start_trial_form.invalid_business_email', defaultMessage: 'Please enter a valid business email address.'},
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
trackEvent(TELEMETRY_CATEGORIES.SELF_HOSTED_START_TRIAL_MODAL, 'form_opened');
|
||||
if (email && !didOnce) {
|
||||
handleValidateBusinessEmail(email);
|
||||
}
|
||||
}, [email, didOnce]);
|
||||
|
||||
if (!show) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const openTrialBenefitsModal = () => {
|
||||
dispatch(openModal({
|
||||
modalId: ModalIdentifiers.TRIAL_BENEFITS_MODAL,
|
||||
dialogType: TrialBenefitsModal,
|
||||
dialogProps: {trialJustStarted: true},
|
||||
}));
|
||||
};
|
||||
|
||||
// Reset the TrialLoadStatus so the user can re-submit the form
|
||||
const handleErrorModalTryAgain = () => {
|
||||
dispatch(closeModal(ModalIdentifiers.START_TRIAL_FORM_MODAL_RESULT));
|
||||
setLoadStatus(TrialLoadStatus.NotStarted);
|
||||
};
|
||||
|
||||
const requestLicense = async () => {
|
||||
setLoadStatus(TrialLoadStatus.Started);
|
||||
const requestedUsers = Math.max(totalUsers, 30);
|
||||
const trialRequestBody = {
|
||||
users: requestedUsers,
|
||||
terms_accepted: true,
|
||||
receive_emails_accepted: true,
|
||||
contact_name: name,
|
||||
contact_email: email,
|
||||
company_name: companyName,
|
||||
company_country: country,
|
||||
company_size: orgSize,
|
||||
};
|
||||
const error = await dispatch(requestTrialLicense(trialRequestBody, props.page || 'license'));
|
||||
if (error) {
|
||||
setLoadStatus(TrialLoadStatus.Failed);
|
||||
let title;
|
||||
let subtitle;
|
||||
let buttonText;
|
||||
let onTryAgain = handleErrorModalTryAgain;
|
||||
|
||||
if (error?.data.status === 422) {
|
||||
title = (<></>);
|
||||
subtitle = (
|
||||
<FormattedMessage
|
||||
id='admin.license.trial-request.embargoed'
|
||||
defaultMessage='We were unable to process the request due to limitations for embargoed countries. <link>Learn more in our documentation</link>, or reach out to legal@mattermost.com for questions around export limitations.'
|
||||
values={{
|
||||
link: (text: string) => (
|
||||
<ExternalLink
|
||||
location='trial_banner'
|
||||
href={LicenseLinks.EMBARGOED_COUNTRIES}
|
||||
>
|
||||
{text}
|
||||
</ExternalLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
buttonText = (
|
||||
<FormattedMessage
|
||||
id='admin.license.trial-request.embargoed.button'
|
||||
defaultMessage='Close'
|
||||
/>
|
||||
);
|
||||
onTryAgain = handleOnClose;
|
||||
}
|
||||
dispatch(openModal({
|
||||
modalId: ModalIdentifiers.START_TRIAL_FORM_MODAL_RESULT,
|
||||
dialogType: StartTrialFormModalResult,
|
||||
dialogProps: {
|
||||
onTryAgain,
|
||||
title,
|
||||
subtitle,
|
||||
buttonText,
|
||||
},
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
setLoadStatus(TrialLoadStatus.Success);
|
||||
await dispatch(getLicenseConfig());
|
||||
dispatch(closeModal(ModalIdentifiers.START_TRIAL_FORM_MODAL));
|
||||
openTrialBenefitsModal();
|
||||
};
|
||||
|
||||
const btnText = (status: TrialLoadStatus): string => {
|
||||
switch (status) {
|
||||
case TrialLoadStatus.Started:
|
||||
return formatMessage({id: 'start_trial.modal.loading', defaultMessage: 'Loading...'});
|
||||
case TrialLoadStatus.Success:
|
||||
return formatMessage({id: 'start_trial.modal.loaded', defaultMessage: 'Loaded!'});
|
||||
case TrialLoadStatus.Failed:
|
||||
return formatMessage({id: 'start_trial.modal.failed', defaultMessage: 'Failed'});
|
||||
default:
|
||||
return formatMessage({id: 'start_trial_form.modal_btn.start', defaultMessage: 'Start trial'});
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnClose = () => {
|
||||
if (props.onClose) {
|
||||
props.onClose();
|
||||
}
|
||||
trackEvent(TELEMETRY_CATEGORIES.SELF_HOSTED_START_TRIAL_MODAL, 'form_closed');
|
||||
dispatch(closeModal(ModalIdentifiers.START_TRIAL_FORM_MODAL));
|
||||
};
|
||||
|
||||
const getOrgSizeDropdownValue = () => {
|
||||
if (typeof orgSize === 'undefined') {
|
||||
return orgSize;
|
||||
}
|
||||
return {
|
||||
value: orgSize,
|
||||
label: OrgSize[orgSize as unknown as keyof typeof OrgSize],
|
||||
};
|
||||
};
|
||||
|
||||
const isSubmitDisabled = (
|
||||
!name ||
|
||||
!email ||
|
||||
!companyName ||
|
||||
!orgSize ||
|
||||
!country ||
|
||||
Boolean(businessEmailError) ||
|
||||
status === TrialLoadStatus.Started ||
|
||||
status === TrialLoadStatus.Success
|
||||
);
|
||||
|
||||
if (typeof canReachCWS !== 'undefined' && !canReachCWS) {
|
||||
return (
|
||||
<AirGappedModal
|
||||
onClose={handleOnClose}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className={classNames('StartTrialFormModal', {error: TrialLoadStatus.Failed === status})}
|
||||
dialogClassName='a11y__modal'
|
||||
show={show}
|
||||
id='StartTrialFormModal'
|
||||
role='dialog'
|
||||
onHide={handleOnClose}
|
||||
>
|
||||
<Modal.Header closeButton={true}>
|
||||
<div className='title'>
|
||||
<FormattedMessage
|
||||
id='start_trial_form.modal_title'
|
||||
defaultMessage='Start Trial'
|
||||
/>
|
||||
</div>
|
||||
<div className='description'>
|
||||
<FormattedMessage
|
||||
id='start_trial_form.modal_body'
|
||||
defaultMessage='Just a few quick items to help us tailor your trial experience.'
|
||||
/>
|
||||
</div>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<Input
|
||||
className={'name_input'}
|
||||
name='name'
|
||||
type='text'
|
||||
value={name}
|
||||
inputSize={SIZE.LARGE}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
required={true}
|
||||
placeholder={formatMessage({id: 'start_trial_form.name', defaultMessage: 'Name'})}
|
||||
/>
|
||||
<Input
|
||||
className={'email_input'}
|
||||
onBlur={(e) => handleValidateBusinessEmail(e.target.value)}
|
||||
name='email'
|
||||
type='text'
|
||||
value={email}
|
||||
inputSize={SIZE.LARGE}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required={true}
|
||||
placeholder={formatMessage({id: 'start_trial_form.email', defaultMessage: 'Business Email'})}
|
||||
customMessage={businessEmailError}
|
||||
/>
|
||||
<Input
|
||||
className={'company_name_input'}
|
||||
name='company_name'
|
||||
type='text'
|
||||
inputSize={SIZE.LARGE}
|
||||
value={companyName}
|
||||
onChange={(e) => setCompanyName(e.target.value)}
|
||||
required={true}
|
||||
placeholder={formatMessage({id: 'start_trial_form.company_name', defaultMessage: 'Company Name'})}
|
||||
/>
|
||||
<DropdownInput
|
||||
className={'company_size_dropdown'}
|
||||
onChange={(e) => {
|
||||
setOrgSize(e.value as OrgSize);
|
||||
}}
|
||||
value={getOrgSizeDropdownValue()}
|
||||
options={Object.entries(OrgSize).map(([value, label]) => ({value, label}))}
|
||||
legend={formatMessage({id: 'start_trial_form.company_size', defaultMessage: 'Company Size'})}
|
||||
placeholder={formatMessage({id: 'start_trial_form.company_size', defaultMessage: 'Company Size'})}
|
||||
name='company_size_dropdown'
|
||||
/>
|
||||
<div className='countries-section'>
|
||||
<DropdownInput
|
||||
onChange={(e) => setCountry(e.value)}
|
||||
value={
|
||||
country ? {value: country, label: country} : undefined
|
||||
}
|
||||
options={COUNTRIES.map((country) => ({
|
||||
value: country.name,
|
||||
label: country.name,
|
||||
}))}
|
||||
legend={formatMessage({
|
||||
id: 'payment_form.country',
|
||||
defaultMessage: 'Country',
|
||||
})}
|
||||
placeholder={formatMessage({
|
||||
id: 'payment_form.country',
|
||||
defaultMessage: 'Country',
|
||||
})}
|
||||
name={'country_dropdown'}
|
||||
/>
|
||||
</div>
|
||||
<div className='disclaimer'>
|
||||
<FormattedMessage
|
||||
id='start_trial_form.disclaimer'
|
||||
defaultMessage='By selecting Start trial, I agree to the <agreement>Mattermost Software Evaluation Agreement</agreement>, <privacypolicy>Privacy Policy</privacypolicy>, and receiving product emails.'
|
||||
values={{
|
||||
agreement: (msg: React.ReactNode) => (
|
||||
<ExternalLink
|
||||
href='https://mattermost.com/software-evaluation-agreement/'
|
||||
location='start_trial_form_modal'
|
||||
>
|
||||
{msg}
|
||||
</ExternalLink>
|
||||
),
|
||||
privacypolicy: (msg: React.ReactNode) => (
|
||||
<ExternalLink
|
||||
href='https://mattermost.com/privacy-policy/'
|
||||
location='start_trial_form_modal'
|
||||
>
|
||||
{msg}
|
||||
</ExternalLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className='buttons'>
|
||||
<Button
|
||||
disabled={isSubmitDisabled}
|
||||
className='confirm-btn'
|
||||
onClick={requestLicense}
|
||||
>
|
||||
{btnText(status)}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default StartTrialFormModal;
|
||||
@@ -1,10 +1,14 @@
|
||||
.StartTrialModal {
|
||||
.StartTrialFormModal {
|
||||
&.error {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 512px;
|
||||
height: 496px;
|
||||
height: 660px;
|
||||
border: 1 px solid rgba(var(--center-channel-color-rgb), 0.08);
|
||||
margin: auto;
|
||||
border-radius: 8px;
|
||||
@@ -26,10 +30,10 @@
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
top: 6px;
|
||||
right: 4px;
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
top: 26px;
|
||||
right: 26px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 4px;
|
||||
color: rgba(var(--center-channel-color-rgb), 0.56) !important;
|
||||
font-family:
|
||||
@@ -39,9 +43,32 @@
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
height: 48px;
|
||||
.title {
|
||||
font-family: Metropolis, sans-serif;
|
||||
font-size: 22px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 28px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin: 0 auto;
|
||||
margin-top: 8px;
|
||||
color: rgba(var(--center-channel-color-rgb), 0.72);
|
||||
font-family: 'Open Sans';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
line-height: 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
display: block;
|
||||
min-height: 48px;
|
||||
padding: 0;
|
||||
padding-top: 26px;
|
||||
padding-bottom: 0;
|
||||
padding-left: 32px;
|
||||
border: 0;
|
||||
background: var(--center-channel-bg) !important;
|
||||
border-radius: 8px;
|
||||
@@ -53,75 +80,69 @@
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
|
||||
.upgrade-image {
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.title {
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
margin-top: 16px;
|
||||
font-family: Metropolis, sans-serif;
|
||||
font-size: 22px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 28px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.description {
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
margin-top: 8px;
|
||||
font-family: 'Open Sans';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
padding-top: 32px;
|
||||
padding-right: 32px;
|
||||
padding-bottom: 26px;
|
||||
padding-left: 32px;
|
||||
|
||||
.disclaimer {
|
||||
width: 80%;
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
margin: 10px auto;
|
||||
margin: 0;
|
||||
margin-top: 36px;
|
||||
margin-bottom: 32px;
|
||||
color: rgba(var(--center-channel-color-rgb), 0.72);
|
||||
font-family: 'Open Sans';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.email_input {
|
||||
margin-top: 36px;
|
||||
}
|
||||
|
||||
.company_name_input {
|
||||
margin-top: 36px;
|
||||
}
|
||||
|
||||
.DropdownInput {
|
||||
margin-top: 36px;
|
||||
|
||||
.DropDown__menu {
|
||||
z-index: 999;
|
||||
max-height: 124px;
|
||||
}
|
||||
|
||||
.DropDown__menu-list {
|
||||
z-index: 999;
|
||||
max-height: 124px;
|
||||
}
|
||||
}
|
||||
|
||||
.Input___error {
|
||||
height: 100%;
|
||||
font-weight: 600;
|
||||
line-height: 16px;
|
||||
|
||||
.icon.error.icon-alert-circle-outline {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 4px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.countries-section {
|
||||
z-index: 999;
|
||||
max-height: 164px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
margin-top: 32px;
|
||||
margin-bottom: 32px;
|
||||
text-align: center;
|
||||
|
||||
.dismiss-btn {
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus,
|
||||
&:active:focus {
|
||||
border: none;
|
||||
background: rgba(var(--button-bg-rgb), 0.08);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
height: 40px;
|
||||
align-items: center;
|
||||
align-self: center;
|
||||
border: none;
|
||||
background: var(--center-channel-bg);
|
||||
color: var(--button-bg);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 14px;
|
||||
}
|
||||
text-align: right;
|
||||
|
||||
.confirm-btn {
|
||||
&:hover,
|
||||
@@ -0,0 +1,108 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {act, RenderResult, screen} from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import {Provider} from 'react-redux';
|
||||
import mockStore from 'tests/test_store';
|
||||
import {ModalIdentifiers} from 'utils/constants';
|
||||
import StartTrialFormModal from '.';
|
||||
import {BrowserRouter} from 'react-router-dom';
|
||||
import {renderWithIntl} from 'tests/react_testing_utils';
|
||||
import {trackEvent} from 'actions/telemetry_actions';
|
||||
|
||||
jest.mock('actions/telemetry_actions.jsx', () => {
|
||||
const original = jest.requireActual('actions/telemetry_actions.jsx');
|
||||
return {
|
||||
...original,
|
||||
trackEvent: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe('components/start_trial_form_modal/start_trial_form_modal', () => {
|
||||
const state = {
|
||||
entities: {
|
||||
users: {
|
||||
currentUserId: 'user1',
|
||||
profiles: {
|
||||
user1: {
|
||||
id: 'user1',
|
||||
roles: '',
|
||||
email: 'test@mattermost.com',
|
||||
},
|
||||
},
|
||||
},
|
||||
general: {
|
||||
license: {
|
||||
IsLicensed: 'true',
|
||||
Cloud: 'false',
|
||||
},
|
||||
config: {
|
||||
TelemetryId: 'test123',
|
||||
},
|
||||
},
|
||||
},
|
||||
views: {
|
||||
modals: {
|
||||
modalState: {
|
||||
[ModalIdentifiers.START_TRIAL_FORM_MODAL]: {
|
||||
open: 'true',
|
||||
},
|
||||
},
|
||||
},
|
||||
admin: {
|
||||
navigationBlock: {
|
||||
blocked: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const handleOnClose = jest.fn();
|
||||
|
||||
const props = {
|
||||
onClose: handleOnClose,
|
||||
page: 'some_modal',
|
||||
};
|
||||
|
||||
test('should match snapshot', async () => {
|
||||
const store = await mockStore(state);
|
||||
let wrapper: RenderResult | HTMLElement | null;
|
||||
await act(async () => {
|
||||
wrapper = await renderWithIntl(
|
||||
<Provider store={store}>
|
||||
<BrowserRouter>
|
||||
<StartTrialFormModal {...props}/>
|
||||
</BrowserRouter>
|
||||
</Provider>);
|
||||
});
|
||||
expect(wrapper!).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should pre-fill email, fire trackEvent', async () => {
|
||||
const store = await mockStore(state);
|
||||
await act(async () => {
|
||||
await renderWithIntl(
|
||||
<Provider store={store}>
|
||||
<BrowserRouter>
|
||||
<StartTrialFormModal {...props}/>
|
||||
</BrowserRouter>
|
||||
</Provider>);
|
||||
});
|
||||
expect(screen.getByDisplayValue('test@mattermost.com')).toBeInTheDocument();
|
||||
expect(trackEvent).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('Start trial button should be disabled on load', async () => {
|
||||
const store = await mockStore(state);
|
||||
await act(async () => {
|
||||
await renderWithIntl(
|
||||
<Provider store={store}>
|
||||
<BrowserRouter>
|
||||
<StartTrialFormModal {...props}/>
|
||||
</BrowserRouter>
|
||||
</Provider>);
|
||||
});
|
||||
expect(screen.getByRole('button', {name: 'Start trial'})).toBeDisabled();
|
||||
});
|
||||
});
|
||||
@@ -1,165 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useState} from 'react';
|
||||
import {useSelector, useDispatch} from 'react-redux';
|
||||
import {Modal, Button} from 'react-bootstrap';
|
||||
import {FormattedMessage, useIntl} from 'react-intl';
|
||||
|
||||
import {isModalOpen} from 'selectors/views/modals';
|
||||
import {GlobalState} from 'types/store';
|
||||
import {closeModal, openModal} from 'actions/views/modals';
|
||||
import {requestTrialLicense} from 'actions/admin_actions';
|
||||
import {getLicenseConfig} from 'mattermost-redux/actions/general';
|
||||
import {DispatchFunc} from 'mattermost-redux/types/actions';
|
||||
|
||||
import {makeAsyncComponent} from 'components/async_load';
|
||||
import useGetTotalUsersNoBots from 'components/common/hooks/useGetTotalUsersNoBots';
|
||||
import ExternalLink from 'components/external_link';
|
||||
|
||||
import {AboutLinks, LicenseLinks, ModalIdentifiers} from 'utils/constants';
|
||||
|
||||
import StartTrialModalSvg from './start_trial_modal_svg';
|
||||
|
||||
const TrialBenefitsModal = makeAsyncComponent('TrialBenefisModal', React.lazy(() => import('components/trial_benefits_modal/trial_benefits_modal')));
|
||||
|
||||
import './start_trial_modal.scss';
|
||||
|
||||
enum TrialLoadStatus {
|
||||
NotStarted = 'NOT_STARTED',
|
||||
Started = 'STARTED',
|
||||
Success = 'SUCCESS',
|
||||
Failed = 'FAILED'
|
||||
}
|
||||
|
||||
type Props = {
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
function StartTrialModal(props: Props): JSX.Element | null {
|
||||
const [status, setLoadStatus] = useState(TrialLoadStatus.NotStarted);
|
||||
const dispatch = useDispatch<DispatchFunc>();
|
||||
|
||||
const {formatMessage} = useIntl();
|
||||
const show = useSelector((state: GlobalState) => isModalOpen(state, ModalIdentifiers.START_TRIAL_MODAL));
|
||||
const totalUsers = useGetTotalUsersNoBots(true) || 0;
|
||||
|
||||
const openTrialBenefitsModal = async () => {
|
||||
await dispatch(openModal({
|
||||
modalId: ModalIdentifiers.TRIAL_BENEFITS_MODAL,
|
||||
dialogType: TrialBenefitsModal,
|
||||
dialogProps: {trialJustStarted: true},
|
||||
}));
|
||||
};
|
||||
|
||||
const requestLicense = async () => {
|
||||
setLoadStatus(TrialLoadStatus.Started);
|
||||
const requestedUsers = Math.max(totalUsers, 30);
|
||||
const {error} = await dispatch(requestTrialLicense(requestedUsers, true, true, 'license'));
|
||||
if (error) {
|
||||
setLoadStatus(TrialLoadStatus.Failed);
|
||||
}
|
||||
|
||||
setLoadStatus(TrialLoadStatus.Success);
|
||||
await dispatch(getLicenseConfig());
|
||||
await dispatch(closeModal(ModalIdentifiers.START_TRIAL_MODAL));
|
||||
openTrialBenefitsModal();
|
||||
};
|
||||
|
||||
const btnText = (status: TrialLoadStatus): string => {
|
||||
switch (status) {
|
||||
case TrialLoadStatus.Started:
|
||||
return formatMessage({id: 'start_trial.modal.loading', defaultMessage: 'Loading...'});
|
||||
case TrialLoadStatus.Success:
|
||||
return formatMessage({id: 'start_trial.modal.loaded', defaultMessage: 'Loaded!'});
|
||||
case TrialLoadStatus.Failed:
|
||||
return formatMessage({id: 'start_trial.modal.failed', defaultMessage: 'Failed'});
|
||||
default:
|
||||
return formatMessage({id: 'start_trial.modal_btn.start', defaultMessage: 'Start 30-day trial'});
|
||||
}
|
||||
};
|
||||
|
||||
if (!show) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleOnClose = () => {
|
||||
if (props.onClose) {
|
||||
props.onClose();
|
||||
}
|
||||
dispatch(closeModal(ModalIdentifiers.START_TRIAL_MODAL));
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className='StartTrialModal'
|
||||
dialogClassName='a11y__modal'
|
||||
show={show}
|
||||
id='startTrialModal'
|
||||
role='dialog'
|
||||
onHide={handleOnClose}
|
||||
>
|
||||
<Modal.Header closeButton={true}/>
|
||||
<Modal.Body>
|
||||
<StartTrialModalSvg/>
|
||||
<div className='title'>
|
||||
<FormattedMessage
|
||||
id='start_trial.modal_title'
|
||||
defaultMessage='Start your free Enterprise trial now'
|
||||
/>
|
||||
</div>
|
||||
<div className='description'>
|
||||
<FormattedMessage
|
||||
id='start_trial.modal_body'
|
||||
defaultMessage='Access all platform features including advanced security and enterprise compliance.'
|
||||
/>
|
||||
</div>
|
||||
<div className='buttons'>
|
||||
<Button
|
||||
className='dismiss-btn'
|
||||
onClick={handleOnClose}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='start_trial.modal_btn.nottnow'
|
||||
defaultMessage='Not now'
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
className='confirm-btn'
|
||||
onClick={requestLicense}
|
||||
>
|
||||
{btnText(status)}
|
||||
</Button>
|
||||
</div>
|
||||
<div className='disclaimer'>
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id='start_trial.modal.disclaimer'
|
||||
defaultMessage='By clicking “Start free 30-day trial”, I agree to the <linkEvaluation>Mattermost Software and Services License Agreement</linkEvaluation>, <linkPrivacy>privacy policy</linkPrivacy> and receiving product emails.'
|
||||
values={{
|
||||
linkEvaluation: (msg: React.ReactNode) => (
|
||||
<ExternalLink
|
||||
href={LicenseLinks.SOFTWARE_SERVICES_LICENSE_AGREEMENT}
|
||||
location='start_trial_modal'
|
||||
>
|
||||
{msg}
|
||||
</ExternalLink>
|
||||
),
|
||||
linkPrivacy: (msg: React.ReactNode) => (
|
||||
<ExternalLink
|
||||
href={AboutLinks.PRIVACY_POLICY}
|
||||
location='start_trial_modal'
|
||||
>
|
||||
{msg}
|
||||
</ExternalLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default StartTrialModal;
|
||||
@@ -1,308 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {FC} from 'react';
|
||||
|
||||
const StartTrialModalSvg: FC = () => (
|
||||
<svg
|
||||
width='512'
|
||||
height='156'
|
||||
viewBox='0 0 512 156'
|
||||
display='inline-block'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<g
|
||||
clipPath='url(#clip0)'
|
||||
style={{transform: 'translate(135px, 10px)'}}
|
||||
>
|
||||
<path
|
||||
d='M117.504 138.565C156.52 138.565 188.158 107.604 188.158 69.4C188.158 31.1964 156.52 0.235336 117.504 0.235336C78.4886 0.235336 46.851 31.1964 46.851 69.4C46.851 107.604 78.4886 138.565 117.504 138.565Z'
|
||||
fill='#E8E9ED'
|
||||
stroke='#E8E9ED'
|
||||
strokeWidth='0.5'
|
||||
/>
|
||||
<path
|
||||
d='M97.7842 49.8971C100.351 52.3506 101.639 55.4564 101.639 59.2294C101.639 63.0023 100.351 66.1155 97.7842 68.5616C95.2171 71.0151 92.0367 72.2974 88.2504 72.4309H17.4471C12.4417 72.2974 8.28436 70.6148 4.96759 67.3682C1.65839 64.129 0 60.0596 0 55.1599C0 50.2603 1.65839 46.1909 4.96759 42.9443C8.27679 39.7051 12.4417 38.015 17.4471 37.8816C17.5834 31.3957 19.8476 25.9995 24.2473 21.7003C28.6394 17.4011 34.1522 15.1848 40.7781 15.0514C46.3212 15.044 51.1525 16.6302 55.2796 19.8101C59.4066 22.9901 62.1403 27.0891 63.4958 32.1222C65.7902 30.5359 68.4331 29.7354 71.4091 29.7354C75.3316 29.8688 78.6106 31.2253 81.2458 33.8048C83.8811 36.3843 85.2668 39.6013 85.4031 43.4335C85.4031 44.4935 85.2668 45.4867 84.9942 46.4133C86.0771 46.1465 87.1524 46.013 88.2428 46.013C92.0291 46.1539 95.2096 47.4436 97.7842 49.8971Z'
|
||||
fill='#FFBC1F'
|
||||
/>
|
||||
<path
|
||||
d='M101.639 59.2301C101.639 55.4572 100.351 52.3514 97.7842 49.8979C95.2171 47.4518 92.0367 46.1621 88.2504 46.0286C87.1675 46.0286 86.0847 46.1621 85.0018 46.4289C85.2744 45.5024 85.4107 44.5091 85.4107 43.4491C85.2744 39.6095 83.8886 36.3999 81.2534 33.8204C78.6181 31.2409 75.3316 29.8696 71.4091 29.7361C68.4331 29.7361 65.7978 30.5293 63.4958 32.1229C62.1403 27.0899 59.4066 22.9908 55.2796 19.8109C51.1525 16.631 46.3212 15.0447 40.7781 15.0447C34.1522 15.1782 28.6394 17.3945 24.2473 21.6937C19.8476 26.0003 17.5834 31.3965 17.4471 37.875C12.4417 38.0084 8.28436 39.6984 4.96759 42.9377C1.65839 46.1917 0 50.2611 0 55.1607C0 55.1607 0.21203 42.6412 18.5906 39.6169C19.2797 22.4572 34.5687 17.7503 40.9296 17.7503C47.2905 17.7503 57.7028 20.7745 63.0338 34.3986C74.2563 27.505 86.804 35.9033 83.3661 48.5266C98.9958 45.1614 101.639 59.2301 101.639 59.2301Z'
|
||||
fill='#F5AB00'
|
||||
/>
|
||||
<path
|
||||
d='M225.894 63.9064C227.346 65.2949 228.075 67.0526 228.075 69.1878C228.075 71.323 227.346 73.0848 225.894 74.4691C224.441 75.8576 222.641 76.5834 220.498 76.6589H180.429C177.596 76.5834 175.243 75.6311 173.366 73.7938C171.493 71.9606 170.555 69.6576 170.555 66.8848C170.555 64.112 171.493 61.809 173.366 59.9716C175.239 58.1385 177.596 57.182 180.429 57.1065C180.506 53.436 181.787 50.3821 184.277 47.9491C186.763 45.5161 189.882 44.2618 193.632 44.1863C196.769 44.1821 199.503 45.0798 201.839 46.8794C204.175 48.679 205.722 50.9988 206.489 53.8471C207.787 52.9494 209.283 52.4963 210.967 52.4963C213.187 52.5718 215.043 53.3395 216.534 54.7993C218.025 56.2591 218.81 58.0797 218.887 60.2485C218.887 60.8484 218.81 61.4105 218.655 61.9348C219.268 61.7838 219.877 61.7083 220.494 61.7083C222.637 61.788 224.436 62.5179 225.894 63.9064Z'
|
||||
fill='#FFBC1F'
|
||||
/>
|
||||
<path
|
||||
d='M228.075 69.189C228.075 67.0538 227.346 65.2961 225.894 63.9076C224.441 62.5233 222.641 61.7934 220.498 61.7179C219.885 61.7179 219.272 61.7934 218.66 61.9444C218.814 61.42 218.891 60.8579 218.891 60.2581C218.814 58.0851 218.03 56.2687 216.538 54.8089C215.047 53.3491 213.187 52.573 210.967 52.4975C209.283 52.4975 207.792 52.9464 206.489 53.8483C205.722 50.9999 204.175 48.6802 201.839 46.8806C199.503 45.081 196.769 44.1833 193.632 44.1833C189.882 44.2588 186.763 45.513 184.277 47.9461C181.787 50.3833 180.506 53.4372 180.429 57.1035C177.596 57.179 175.243 58.1354 173.366 59.9686C171.493 61.8102 170.555 64.1132 170.555 66.886C170.555 66.886 170.675 59.8008 181.076 58.0893C181.466 48.3781 190.118 45.7144 193.718 45.7144C197.318 45.7144 203.21 47.4259 206.227 55.1361C212.579 51.2349 219.68 55.9877 217.734 63.1316C226.579 61.2271 228.075 69.189 228.075 69.189Z'
|
||||
fill='#F5AB00'
|
||||
/>
|
||||
<path
|
||||
d='M199.061 63.0908L190.387 73.9565H194.064V92.2097H203.809V73.9702V73.9565H207.74L199.061 63.0908Z'
|
||||
fill='#386FE5'
|
||||
/>
|
||||
<path
|
||||
d='M49.3268 40.2811L28.7566 65.8203H37.4771V108.225H60.5857V65.8523V65.8203H69.9079L49.3268 40.2811Z'
|
||||
fill='#386FE5'
|
||||
/>
|
||||
<mask
|
||||
id='mask0'
|
||||
mask-type='alpha'
|
||||
maskUnits='userSpaceOnUse'
|
||||
x='47'
|
||||
y='0'
|
||||
width='141'
|
||||
height='139'
|
||||
>
|
||||
<path
|
||||
d='M117.504 138.315C156.387 138.315 187.908 107.46 187.908 69.4C187.908 31.3395 156.387 0.485336 117.504 0.485336C78.6216 0.485336 47.101 31.3395 47.101 69.4C47.101 107.46 78.6216 138.315 117.504 138.315Z'
|
||||
fill='#E8E9ED'
|
||||
/>
|
||||
</mask>
|
||||
<g mask='url(#mask0)'>
|
||||
<path
|
||||
d='M54.6396 94.6503C55.2591 91.4217 56.4956 88.343 57.7274 85.2854C59.0477 82.0077 60.3799 78.7019 62.2527 75.6864C66.6201 68.6627 73.7333 63.594 81.3871 60.2062C89.0408 56.8185 96.3645 54.9034 104.554 53.0679C101.87 55.8961 101.052 59.4968 99.4475 63.0227C95.702 71.2498 92.4826 79.6945 89.2656 88.1323C84.4358 100.792 79.6084 113.45 74.7833 126.107C64.3096 120.069 56.785 109.348 54.7855 97.6119L54.6396 94.6503Z'
|
||||
fill='white'
|
||||
/>
|
||||
<path
|
||||
d='M134.964 53.3651C139.63 54.8775 146.923 57.5418 146.923 57.5418C146.923 57.5418 163.665 104.783 167.375 127.544C167.485 128.214 167.583 128.949 167.268 129.544C166.806 130.419 165.689 130.686 164.778 131.105C162.69 132.061 161.369 134.102 159.561 135.5C158.002 136.701 156.107 137.401 154.244 138.075C137.659 144.026 119.647 149.004 102.522 144.759C101.438 144.49 100.319 144.162 99.4936 143.422C98.776 142.769 98.3383 141.861 97.9437 140.974C92.7272 129.267 82.4831 105.083 82.5835 87.973C82.7007 68.2037 93.6456 56.1534 93.825 55.8983C94.2531 55.2919 110.271 51.1947 120.135 50.9817C120.144 50.9864 131.041 52.0914 134.964 53.3651Z'
|
||||
fill='white'
|
||||
/>
|
||||
<mask
|
||||
id='mask1'
|
||||
mask-type='alpha'
|
||||
maskUnits='userSpaceOnUse'
|
||||
x='82'
|
||||
y='50'
|
||||
width='86'
|
||||
height='97'
|
||||
>
|
||||
<path
|
||||
d='M134.964 53.3651C139.63 54.8775 146.923 57.5418 146.923 57.5418C146.923 57.5418 163.665 104.783 167.375 127.544C167.485 128.214 167.583 128.949 167.268 129.544C166.806 130.419 165.689 130.686 164.778 131.105C162.69 132.061 161.369 134.102 159.561 135.5C158.002 136.701 156.107 137.401 154.244 138.075C137.659 144.026 119.647 149.004 102.522 144.759C101.438 144.49 100.319 144.162 99.4936 143.422C98.776 142.769 98.3383 141.861 97.9437 140.974C92.7272 129.267 82.4831 105.083 82.5835 87.973C82.7007 68.2037 93.6456 56.1534 93.825 55.8983C94.2531 55.2919 110.271 51.1947 120.135 50.9817C120.144 50.9864 131.041 52.0914 134.964 53.3651Z'
|
||||
fill='white'
|
||||
/>
|
||||
</mask>
|
||||
<g mask='url(#mask1)'>
|
||||
<rect
|
||||
x='82.3802'
|
||||
y='50.9582'
|
||||
width='85.148'
|
||||
height='82.6451'
|
||||
fill='url(#pattern0)'
|
||||
/>
|
||||
<rect
|
||||
x='82.3802'
|
||||
y='50.9582'
|
||||
width='74.6241'
|
||||
height='95.5218'
|
||||
fill='url(#pattern1)'
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d='M84.3219 100.889C83.7064 98.6605 83.0902 96.4317 82.4731 94.2028C82.4443 94.1173 82.3831 94.0459 82.3021 94.0033C82.221 93.9607 82.1264 93.9502 82.0377 93.974C81.949 93.9977 81.873 94.0539 81.8254 94.1309C81.7778 94.2079 81.7622 94.2998 81.7818 94.3878C82.3989 96.6166 83.0152 98.8462 83.6307 101.077C83.6582 101.164 83.7195 101.237 83.8013 101.281C83.8832 101.325 83.9793 101.336 84.0692 101.311C84.1591 101.287 84.2358 101.229 84.283 101.151C84.3302 101.072 84.3441 100.978 84.3219 100.889Z'
|
||||
fill='#1D1400'
|
||||
/>
|
||||
<path
|
||||
d='M81.4944 92.3346L81.3222 91.5573C81.2934 91.4718 81.2321 91.4004 81.1511 91.3578C81.0701 91.3152 80.9755 91.3047 80.8868 91.3284C80.7981 91.3522 80.7221 91.4083 80.6745 91.4853C80.6268 91.5623 80.6113 91.6543 80.6309 91.7422L80.8031 92.5219C80.8294 92.6108 80.89 92.6863 80.9722 92.7322C81.0544 92.7781 81.1516 92.7908 81.2432 92.7677C81.3317 92.7386 81.4057 92.6777 81.4501 92.5973C81.4945 92.5169 81.506 92.423 81.4824 92.3346H81.4944Z'
|
||||
fill='#1D1400'
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d='M145.561 57.0946C150.823 58.7334 168.01 64.4273 172.009 68.5853C186.157 83.2952 191.928 122.918 192.376 126.608C193.021 131.789 191.558 143.465 186.157 146.742C183.198 148.538 174.437 142.762 169.893 138.08C163.562 131.555 160.483 114.845 159.213 105.932C156.884 89.586 145.561 57.0946 145.561 57.0946Z'
|
||||
fill='white'
|
||||
/>
|
||||
<path
|
||||
d='M122.065 51.2391L151.268 49.5137C151.268 49.5137 152.225 45.5547 149.439 40.9612C147.829 38.3062 144.949 36.8617 143.06 33.2C141.247 29.6882 141.479 25.6262 140.752 22.0277C139.199 14.3392 133.5 8.19112 130.639 6.88004C123.782 3.73578 122.065 51.2391 122.065 51.2391Z'
|
||||
fill='#1D1400'
|
||||
/>
|
||||
<path
|
||||
d='M112.684 49.5534C108.962 52.611 111.155 68.55 119.529 68.55C128.702 68.55 128.075 46.3084 128.737 46.175C131.78 45.592 134.904 43.9953 136.843 36.9014C137.121 35.89 137.126 35.2626 137.305 34.305C137.398 33.8157 138.111 27.1572 137.912 19.8667C137.824 16.5515 134.456 6.36955 122.289 8.95894C107.077 12.1945 109.696 26.6352 111.177 29.7326C112.045 31.547 113.275 36.0258 113.48 38.4255C113.731 41.317 113.1 49.2092 112.684 49.5534Z'
|
||||
fill='#CB8E00'
|
||||
/>
|
||||
<path
|
||||
opacity='0.3'
|
||||
d='M128.711 46.154C128.045 46.2766 127.369 46.3393 126.69 46.3413C124.979 46.3388 123.29 45.9587 121.749 45.2292H121.73C121.694 45.2131 121.657 45.1991 121.62 45.1871C120.955 44.9857 120.61 45.9573 121.24 46.2476C122.483 46.8343 123.629 47.5997 124.641 48.5186C125.678 49.4739 126.494 50.6364 127.032 51.9274C127.373 52.5704 127.651 53.2429 127.865 53.9362C128.403 49.7407 128.468 46.2593 128.726 46.1868L128.711 46.154Z'
|
||||
fill='black'
|
||||
/>
|
||||
<path
|
||||
d='M111.636 7.62455C117.415 2.56985 125.549 4.52243 129.096 6.05827C135.188 8.69917 137.037 14.8332 137.037 14.8332C135.626 16.5446 136.236 18.0828 134.667 20.1828C132.835 22.6388 130.584 20.5855 126.838 23.5565C125.306 24.7888 124.061 26.3279 123.186 28.0704C123.186 28.0704 121.926 25.6004 120.395 25.6215C118.642 25.6473 117.585 27.2604 118.07 28.8992C118.422 30.0815 119.804 32.8114 119.804 32.8114L114.32 46.5544C113.754 47.9729 112.686 49.1455 111.311 49.8587C109.936 50.5719 108.346 50.7785 106.829 50.4408L74.2858 40.2167C77.3545 33.3382 82.4706 32.4743 86.3811 29.1872C90.208 25.961 91.1839 22.6622 94.3817 20.1992C98.1536 17.2914 104.042 15.6011 105.697 13.9856C110.402 9.39217 107.977 10.825 111.636 7.62455Z'
|
||||
fill='#1D1400'
|
||||
/>
|
||||
<mask
|
||||
id='mask2'
|
||||
mask-type='alpha'
|
||||
maskUnits='userSpaceOnUse'
|
||||
x='74'
|
||||
y='4'
|
||||
width='64'
|
||||
height='47'
|
||||
>
|
||||
<path
|
||||
d='M111.636 7.62455C117.415 2.56985 125.549 4.52243 129.096 6.05827C135.188 8.69917 137.037 14.8332 137.037 14.8332C135.626 16.5446 136.236 18.0828 134.667 20.1828C132.835 22.6388 130.584 20.5855 126.838 23.5565C125.306 24.7888 124.061 26.3279 123.186 28.0704C123.186 28.0704 121.926 25.6004 120.395 25.6215C118.642 25.6473 117.585 27.2604 118.07 28.8992C118.422 30.0815 119.804 32.8114 119.804 32.8114L114.32 46.5544C113.754 47.9729 112.686 49.1455 111.311 49.8587C109.936 50.5719 108.346 50.7785 106.829 50.4408L74.2858 40.2167C77.3545 33.3382 82.4706 32.4743 86.3811 29.1872C90.208 25.961 91.1839 22.6622 94.3817 20.1992C98.1536 17.2914 104.042 15.6011 105.697 13.9856C110.402 9.39217 107.977 10.825 111.636 7.62455Z'
|
||||
fill='#1D1400'
|
||||
/>
|
||||
</mask>
|
||||
<g mask='url(#mask2)'>
|
||||
<rect
|
||||
x='74.2472'
|
||||
y='4.36786'
|
||||
width='53.3371'
|
||||
height='46.3562'
|
||||
fill='url(#pattern2)'
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d='M132.08 67.6581C130.776 64.1323 129.927 60.5385 131.051 56.9494C132.534 52.171 134.321 51.9509 138.868 49.6916C143.414 47.4323 144.18 46.1517 146.954 41.3288C147.607 40.1909 148.289 40.4321 148.425 41.2937C148.545 42.0522 148.425 43.6349 148.425 43.6349C148.425 43.6349 150.339 37.7818 151.056 37.5477C152.436 37.0958 152.783 39.9545 152.97 40.5913C153.792 43.4125 153.209 44.8055 151.295 49.9562C150.049 53.3065 147.325 57.3357 144.228 59.2555C143.032 59.9953 141.597 60.8733 141.513 62.7673C141.482 63.4931 142.018 64.1229 142.446 64.7152C144.94 68.1545 164.998 91.3138 167.923 94.0601C177.452 103.008 185.67 109.709 189.564 122.061C191.274 127.49 189.803 141.493 188.368 144.537C185.02 151.631 172.822 142.196 167.799 135.874C156.636 121.822 136.519 79.6803 132.08 67.6581Z'
|
||||
fill='#CB8E00'
|
||||
/>
|
||||
<mask
|
||||
id='mask3'
|
||||
mask-type='alpha'
|
||||
maskUnits='userSpaceOnUse'
|
||||
x='130'
|
||||
y='37'
|
||||
width='61'
|
||||
height='111'
|
||||
>
|
||||
<path
|
||||
d='M132.08 67.6581C130.776 64.1323 129.927 60.5385 131.051 56.9494C132.534 52.171 134.321 51.9509 138.868 49.6916C143.414 47.4323 144.18 46.1517 146.954 41.3288C147.607 40.1909 148.289 40.4321 148.425 41.2937C148.545 42.0522 148.425 43.6349 148.425 43.6349C148.425 43.6349 150.339 37.7818 151.056 37.5477C152.436 37.0958 152.783 39.9545 152.97 40.5913C153.792 43.4125 153.209 44.8055 151.295 49.9562C150.049 53.3065 147.325 57.3357 144.228 59.2555C143.032 59.9953 141.597 60.8733 141.513 62.7673C141.482 63.4931 142.018 64.1229 142.446 64.7152C144.94 68.1545 164.998 91.3138 167.923 94.0601C177.452 103.008 185.67 109.709 189.564 122.061C191.274 127.49 189.803 141.493 188.368 144.537C185.02 151.631 172.822 142.196 167.799 135.874C156.636 121.822 136.519 79.6803 132.08 67.6581Z'
|
||||
fill='#674600'
|
||||
/>
|
||||
</mask>
|
||||
<g mask='url(#mask3)'>
|
||||
<rect
|
||||
x='130.456'
|
||||
y='37.3792'
|
||||
width='37.5512'
|
||||
height='76.0897'
|
||||
fill='url(#pattern3)'
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d='M190.041 122.059C186.147 109.706 177.451 103.006 167.922 94.0577C167.343 93.5146 166.099 92.1848 164.454 90.3539C164.353 90.464 164.255 90.588 164.155 90.684C160.768 94.3644 156.587 97.0053 151.885 98.7542C150.256 99.3606 147.247 99.9553 145.561 100.18C152.669 114.298 161.043 129.515 166.762 136.703C171.785 143.024 185.499 151.619 188.845 144.525C190.28 141.491 191.751 127.488 190.041 122.059Z'
|
||||
fill='white'
|
||||
/>
|
||||
<path
|
||||
d='M95.0299 134.636C97.426 135.825 99.7521 137.144 101.997 138.588C105.286 140.749 114.824 140.187 114.824 140.187C114.824 140.187 119.692 150.238 120.194 146.827C120.455 145.05 119.677 143.999 119.445 142.226C120.436 143.502 121.036 144.186 121.526 145.715C122.017 147.244 121.84 148.62 121.619 150.163C121.452 151.322 122.045 151.844 122.665 151.898C123.861 151.999 124.1 150.664 124.698 150.475C129.802 148.836 128.749 146.981 129.201 142.409C129.163 147.976 128.845 147.508 126.532 150.709C126.068 151.35 126.932 152.113 129.163 150.863C130.916 149.849 132.103 148.756 132.911 146.181C133.787 143.51 133.582 140.612 132.337 138.085C129.57 132.543 127.367 130.698 121.11 129.949C114.853 129.2 107.63 128.65 103.172 130.183C101.44 130.78 95.0299 134.636 95.0299 134.636Z'
|
||||
fill='#CB8E00'
|
||||
/>
|
||||
<path
|
||||
d='M182.266 107.644C176.867 101.505 171.325 95.4961 165.639 89.6165C165.572 89.5567 165.484 89.524 165.393 89.525C165.303 89.5259 165.216 89.5603 165.15 89.6214C165.084 89.6825 165.045 89.7657 165.039 89.8542C165.033 89.9428 165.062 90.0301 165.12 90.0988C170.804 95.9846 176.347 101.994 181.747 108.126C182.049 108.47 182.568 107.986 182.266 107.644Z'
|
||||
fill='#1D1400'
|
||||
/>
|
||||
<path
|
||||
d='M160.311 126.231L160.472 126.388C160.54 126.452 160.631 126.487 160.725 126.487C160.82 126.487 160.911 126.452 160.979 126.388C161.045 126.321 161.083 126.232 161.083 126.139C161.083 126.047 161.045 125.957 160.979 125.891L160.819 125.734C160.786 125.7 160.746 125.672 160.702 125.653C160.658 125.634 160.611 125.623 160.562 125.623C160.514 125.622 160.466 125.63 160.422 125.648C160.377 125.666 160.336 125.692 160.302 125.725C160.268 125.759 160.241 125.799 160.223 125.842C160.205 125.886 160.196 125.933 160.197 125.98C160.198 126.027 160.209 126.074 160.228 126.117C160.248 126.16 160.276 126.199 160.311 126.231Z'
|
||||
fill='white'
|
||||
/>
|
||||
<path
|
||||
d='M160.381 126.746C155.218 118.275 150.381 109.625 145.872 100.796C145.666 100.393 145.047 100.746 145.253 101.152C149.759 109.973 154.595 118.623 159.761 127.102C159.809 127.182 159.888 127.24 159.981 127.264C160.073 127.287 160.171 127.274 160.253 127.227C160.335 127.18 160.395 127.102 160.419 127.012C160.443 126.922 160.429 126.826 160.381 126.746Z'
|
||||
fill='#1D1400'
|
||||
/>
|
||||
<path
|
||||
d='M148.121 43.8619C147.681 45.0332 147.098 46.148 146.385 47.1818C146.126 47.5564 146.748 47.9099 147.004 47.5353C147.748 46.4482 148.355 45.2778 148.815 44.0492C148.972 43.6231 148.281 43.4405 148.121 43.8619Z'
|
||||
fill='black'
|
||||
/>
|
||||
<path
|
||||
d='M145.975 48.7621H146.095C146.19 48.7621 146.281 48.7251 146.348 48.6593C146.416 48.5934 146.453 48.5041 146.453 48.4109C146.453 48.3178 146.416 48.2285 146.348 48.1626C146.281 48.0968 146.19 48.0598 146.095 48.0598H145.975C145.88 48.0598 145.789 48.0968 145.721 48.1626C145.654 48.2285 145.616 48.3178 145.616 48.4109C145.616 48.5041 145.654 48.5934 145.721 48.6593C145.789 48.7251 145.88 48.7621 145.975 48.7621Z'
|
||||
fill='black'
|
||||
/>
|
||||
<path
|
||||
d='M151.529 42.2161C151.112 44.3131 150.312 46.319 149.166 48.137C148.927 48.5233 149.544 48.8745 149.785 48.4906C150.964 46.6214 151.789 44.5593 152.22 42.4034C152.311 41.9609 151.617 41.7713 151.529 42.2161Z'
|
||||
fill='black'
|
||||
/>
|
||||
<path
|
||||
d='M148.965 49.4646C149.06 49.4646 149.151 49.4276 149.219 49.3617C149.286 49.2959 149.324 49.2065 149.324 49.1134C149.324 49.0203 149.286 48.9309 149.219 48.8651C149.151 48.7992 149.06 48.7622 148.965 48.7622C148.87 48.7622 148.779 48.7992 148.711 48.8651C148.644 48.9309 148.606 49.0203 148.606 49.1134C148.606 49.2065 148.644 49.2959 148.711 49.3617C148.779 49.4276 148.87 49.4646 148.965 49.4646Z'
|
||||
fill='black'
|
||||
/>
|
||||
<path
|
||||
d='M119.709 139.555L119.039 138.129C118.845 137.719 118.226 138.075 118.42 138.482C118.641 138.951 118.865 139.426 119.089 139.908C119.283 140.318 119.9 139.962 119.709 139.555Z'
|
||||
fill='#1D1400'
|
||||
/>
|
||||
<path
|
||||
d='M127.446 136.165C128.286 138.026 128.609 140.021 128.661 142.043C128.697 142.846 128.683 143.649 128.621 144.45C128.503 145.635 127.774 146.384 126.946 147.178C126.614 147.494 127.123 147.99 127.453 147.674C128.113 147.049 128.81 146.379 129.128 145.515C129.482 144.579 129.398 143.446 129.388 142.472C129.364 140.177 129.02 137.932 128.068 135.82C127.881 135.408 127.264 135.764 127.449 136.174L127.446 136.165Z'
|
||||
fill='#1D1400'
|
||||
/>
|
||||
<path
|
||||
d='M124.781 142.397C125.072 143.671 125.139 144.985 124.982 146.281C124.93 146.728 125.647 146.726 125.7 146.281C125.86 144.921 125.783 143.544 125.473 142.21C125.372 141.77 124.679 141.957 124.781 142.397Z'
|
||||
fill='#1D1400'
|
||||
/>
|
||||
<path
|
||||
d='M124.59 139.15C124.398 138.299 124.309 137.429 124.325 136.558C124.325 136.106 123.607 136.106 123.607 136.558C123.592 137.492 123.69 138.425 123.899 139.337C124 139.777 124.691 139.59 124.59 139.15Z'
|
||||
fill='#1D1400'
|
||||
/>
|
||||
<path
|
||||
d='M128.768 27.8247H128.701C128.445 27.8247 128.198 27.9246 128.016 28.1024C127.835 28.2802 127.733 28.5214 127.733 28.7729C127.733 29.0243 127.835 29.2655 128.016 29.4433C128.198 29.6212 128.445 29.7211 128.701 29.7211H128.768C129.025 29.7211 129.272 29.6212 129.453 29.4433C129.635 29.2655 129.737 29.0243 129.737 28.7729C129.737 28.5214 129.635 28.2802 129.453 28.1024C129.272 27.9246 129.025 27.8247 128.768 27.8247Z'
|
||||
fill='black'
|
||||
/>
|
||||
<path
|
||||
d='M135.585 29.4471H135.516C135.259 29.4471 135.013 29.5469 134.832 29.7245C134.65 29.9021 134.548 30.143 134.548 30.3942C134.548 30.6453 134.65 30.8862 134.832 31.0638C135.013 31.2414 135.259 31.3412 135.516 31.3412H135.585C135.842 31.3412 136.088 31.2414 136.269 31.0638C136.451 30.8862 136.553 30.6453 136.553 30.3942C136.553 30.143 136.451 29.9021 136.269 29.7245C136.088 29.5469 135.842 29.4471 135.585 29.4471Z'
|
||||
fill='black'
|
||||
/>
|
||||
<path
|
||||
d='M126.41 37.379C127.381 38.4247 128.504 39.3255 129.742 40.0527C129.117 40.8768 127.752 40.9237 126.943 40.2728C126.135 39.6219 125.876 38.4607 126.14 37.4633'
|
||||
fill='black'
|
||||
/>
|
||||
<path
|
||||
d='M131.811 36.8033C132.529 36.6465 134.153 36.6231 134.267 35.6514C134.346 34.9772 133.631 34.0805 133.411 33.4788C133.007 32.4336 132.778 31.3316 132.732 30.2151C132.732 29.6954 131.895 29.693 131.904 30.2151C131.963 31.669 132.303 33.0988 132.904 34.4293C132.997 34.6424 133.289 35.0287 133.301 35.2628C133.332 35.8622 133.244 35.5578 132.823 35.7451C132.462 35.9066 131.976 35.9301 131.586 36.0143C131.065 36.1267 131.287 36.911 131.806 36.7963L131.811 36.8033Z'
|
||||
fill='black'
|
||||
/>
|
||||
<path
|
||||
opacity='0.3'
|
||||
d='M142.445 64.7129C139.192 62.5027 138.618 59.0939 142.445 57.0945C146.272 55.0951 150.671 51.4241 150.671 51.4241C150.671 51.4241 148.568 57.043 144.227 59.2485C139.886 61.4539 142.445 64.7129 142.445 64.7129Z'
|
||||
fill='black'
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<pattern
|
||||
id='pattern0'
|
||||
patternContentUnits='objectBoundingBox'
|
||||
width='1'
|
||||
height='1'
|
||||
>
|
||||
<use
|
||||
transform='scale(0.00280899)'
|
||||
/>
|
||||
</pattern>
|
||||
<pattern
|
||||
id='pattern1'
|
||||
patternContentUnits='objectBoundingBox'
|
||||
width='1'
|
||||
height='1'
|
||||
>
|
||||
<use
|
||||
transform='scale(0.00320513 0.00245098)'
|
||||
/>
|
||||
</pattern>
|
||||
<pattern
|
||||
id='pattern2'
|
||||
patternContentUnits='objectBoundingBox'
|
||||
width='1'
|
||||
height='1'
|
||||
>
|
||||
<use
|
||||
transform='scale(0.0044843 0.00505051)'
|
||||
/>
|
||||
</pattern>
|
||||
<pattern
|
||||
id='pattern3'
|
||||
patternContentUnits='objectBoundingBox'
|
||||
width='1'
|
||||
height='1'
|
||||
>
|
||||
<use
|
||||
transform='scale(0.00636943 0.00307692)'
|
||||
/>
|
||||
</pattern>
|
||||
<clipPath id='clip0'>
|
||||
<rect
|
||||
width='236'
|
||||
height='156'
|
||||
fill='white'
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default StartTrialModalSvg;
|
||||
@@ -72,7 +72,7 @@ describe('component/user_groups_modal/ad_ldap_upsell_banner', () => {
|
||||
);
|
||||
|
||||
expect(wrapper.find('#ad_ldap_upsell_banner')).toHaveLength(1);
|
||||
expect(wrapper.find('.ad-ldap-banner-btn').text()).toEqual('Try free for 30 days');
|
||||
expect(wrapper.find('.ad-ldap-banner-btn').text()).toEqual('Start trial');
|
||||
});
|
||||
|
||||
test('should display for admin users on professional with option to start trial if no cloud trial before', async () => {
|
||||
@@ -108,7 +108,7 @@ describe('component/user_groups_modal/ad_ldap_upsell_banner', () => {
|
||||
await actImmediate(wrapper);
|
||||
|
||||
expect(wrapper.find('#ad_ldap_upsell_banner')).toHaveLength(1);
|
||||
expect(wrapper.find('.ad-ldap-banner-btn').text()).toEqual('Try free for 30 days');
|
||||
expect(wrapper.find('.ad-ldap-banner-btn').text()).toEqual('Start trial');
|
||||
});
|
||||
|
||||
test('should display for admin users on professional with option to contact sales if self-hosted trialed before', () => {
|
||||
|
||||
@@ -93,7 +93,7 @@ function ADLDAPUpsellBanner() {
|
||||
let btn = (
|
||||
<StartTrialBtn
|
||||
btnClass='ad-ldap-banner-btn'
|
||||
message={formatMessage({id: 'adldap_upsell_banner.trial_btn', defaultMessage: 'Try free for 30 days'})}
|
||||
message={formatMessage({id: 'adldap_upsell_banner.trial_btn', defaultMessage: 'Start trial'})}
|
||||
telemetryId={'start_self-hosted_trial_from_adldap_upsell_banner'}
|
||||
renderAsButton={true}
|
||||
onClick={() => setConfirmed(true)}
|
||||
@@ -103,7 +103,7 @@ function ADLDAPUpsellBanner() {
|
||||
btn = (
|
||||
<CloudStartTrialButton
|
||||
extraClass='ad-ldap-banner-btn'
|
||||
message={formatMessage({id: 'adldap_upsell_banner.trial_btn', defaultMessage: 'Try free for 30 days'})}
|
||||
message={formatMessage({id: 'adldap_upsell_banner.trial_btn', defaultMessage: 'Start trial'})}
|
||||
telemetryId={'start_cloud_trial_from_adldap_upsell_banner'}
|
||||
onClick={() => setConfirmed(true)}
|
||||
/>
|
||||
|
||||
@@ -1340,6 +1340,7 @@
|
||||
"admin.license.Trial": "Trial",
|
||||
"admin.license.trial-request.accept-terms": "By clicking <strong>Start trial</strong>, I agree to the <linkEvaluation>Mattermost Software and Services License Agreement</linkEvaluation>, <linkPrivacy>Privacy Policy</linkPrivacy>, and receiving product emails.",
|
||||
"admin.license.trial-request.embargoed": "We were unable to process the request due to limitations for embargoed countries. <link>Learn more in our documentation</link>, or reach out to legal@mattermost.com for questions around export limitations.",
|
||||
"admin.license.trial-request.embargoed.button": "Close",
|
||||
"admin.license.trial-request.startTrial": "Start trial",
|
||||
"admin.license.trial-request.title": "Experience Mattermost Enterprise Edition for free for the next 30 days. No obligation to buy or credit card required. ",
|
||||
"admin.license.trialCard.contactSales": "Contact sales",
|
||||
@@ -2562,6 +2563,9 @@
|
||||
"admin.webserverModeTitle": "Webserver Mode:",
|
||||
"admin.webserverModeUncompressed": "Uncompressed",
|
||||
"admin.webserverModeUncompressedDescription": "The Mattermost server will serve static files uncompressed.",
|
||||
"air_gapped_modal.close": "Close",
|
||||
"air_gapped_modal.description": "To start your trial, please visit {link} and request a trial key.",
|
||||
"air_gapped_modal.title": "Request a trial key",
|
||||
"alert_banner.tooltipCloseBtn": "Close",
|
||||
"analytics.chart.loading": "Loading...",
|
||||
"analytics.chart.meaningful": "Not enough data for a meaningful representation.",
|
||||
@@ -4969,11 +4973,19 @@
|
||||
"start_cloud_trial.modal.failed": "Failed",
|
||||
"start_cloud_trial.modal.gettingTrial": "Getting Trial...",
|
||||
"start_cloud_trial.modal.loaded": "Loaded!",
|
||||
"start_trial.modal_body": "Access all platform features including advanced security and enterprise compliance.",
|
||||
"start_trial.modal_btn.nottnow": "Not now",
|
||||
"start_trial.modal_btn.start": "Start free 30-day trial",
|
||||
"start_trial_form_modal.failureModal.subtitle": "There was an issue processing your trial request.",
|
||||
"start_trial_form_modal.failureModal.subtitle2": "Please try again or contact support.",
|
||||
"start_trial_form_modal.failureModal.title": "Please try again",
|
||||
"start_trial_form.company_name": "Company Name",
|
||||
"start_trial_form.company_size": "Company Size",
|
||||
"start_trial_form.disclaimer": "By selecting Start trial, I agree to the <agreement>Mattermost Software Evaluation Agreement</agreement>, <privacypolicy>Privacy Policy</privacypolicy>, and receiving product emails.",
|
||||
"start_trial_form.email": "Business Email",
|
||||
"start_trial_form.invalid_business_email": "Please enter a valid business email address.",
|
||||
"start_trial_form.modal_body": "Just a few quick items to help us tailor your trial experience",
|
||||
"start_trial_form.modal_btn.start": "Start trial",
|
||||
"start_trial_form.modal_title": "Start Trial",
|
||||
"start_trial_form.name": "Name",
|
||||
"start_trial.modal_btn.start_free_trial": "Start free 30-day trial",
|
||||
"start_trial.modal_title": "Start your free Enterprise trial now",
|
||||
"start_trial.modal.disclaimer": "By clicking “Start free 30-day trial”, I agree to the <linkEvaluation>Mattermost Software Evaluation Agreement</linkEvaluation>, <linkPrivacy>privacy policy</linkPrivacy> and receiving product emails.",
|
||||
"start_trial.modal.failed": "Failed",
|
||||
"start_trial.modal.gettingTrial": "Getting Trial...",
|
||||
|
||||
@@ -21,6 +21,7 @@ import PurchaseModal from 'components/purchase_modal';
|
||||
import {useNotifyAdmin} from 'components/notify_admin_cta/notify_admin_cta';
|
||||
import Timestamp from 'components/timestamp';
|
||||
import Avatar from 'components/widgets/users/avatar';
|
||||
import StartTrialFormModal from 'components/start_trial_form_modal';
|
||||
|
||||
import {openPricingModal} from '../components/global_header/right_controls/plan_upgrade_button';
|
||||
|
||||
@@ -70,6 +71,7 @@ window.Components = {
|
||||
Avatar,
|
||||
imageURLForUser,
|
||||
BotBadge: BotTag,
|
||||
StartTrialFormModal,
|
||||
};
|
||||
|
||||
// This is a prototype of the Product API for use by internal plugins only while we transition to the proper architecture
|
||||
|
||||
@@ -461,6 +461,8 @@ export const ModalIdentifiers = {
|
||||
DELETE_WORKSPACE_RESULT: 'delete_workspace_result',
|
||||
SCREENING_IN_PROGRESS: 'screening_in_progress',
|
||||
CONFIRM_SWITCH_TO_YEARLY: 'confirm_switch_to_yearly',
|
||||
START_TRIAL_FORM_MODAL: 'start_trial_form_modal',
|
||||
START_TRIAL_FORM_MODAL_RESULT: 'start_trial_form_modal_result',
|
||||
};
|
||||
|
||||
export const UserStatuses = {
|
||||
|
||||
@@ -4255,7 +4255,7 @@ export default class Client4 {
|
||||
}
|
||||
|
||||
cwsAvailabilityCheck = () => {
|
||||
return this.doFetch<StatusOK>(
|
||||
return this.doFetchWithResponse(
|
||||
`${this.getCloudRoute()}/check-cws-connection`,
|
||||
{method: 'get'},
|
||||
);
|
||||
|
||||
@@ -248,6 +248,11 @@ export type RequestLicenseBody = {
|
||||
users: number;
|
||||
terms_accepted: boolean;
|
||||
receive_emails_accepted: boolean;
|
||||
contact_name: string;
|
||||
contact_email: string;
|
||||
company_name: string;
|
||||
company_size: string;
|
||||
company_country: string;
|
||||
}
|
||||
|
||||
export type DataRetentionPolicy = {
|
||||
|
||||
@@ -569,24 +569,6 @@ export function exportChannelUrl(channelId: string) {
|
||||
return `${exportPluginUrl}/export${queryParams}`;
|
||||
}
|
||||
|
||||
export async function trackRequestTrialLicense(action: string) {
|
||||
await doFetchWithoutResponse(`${apiUrl}/telemetry/start-trial`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({action}),
|
||||
});
|
||||
}
|
||||
|
||||
export const requestTrialLicense = async (users: number, action: string) => {
|
||||
trackRequestTrialLicense(action);
|
||||
|
||||
try {
|
||||
const response = await Client4.requestTrialLicense({users, terms_accepted: true, receive_emails_accepted: true});
|
||||
return {data: response};
|
||||
} catch (e) {
|
||||
return {error: e.message};
|
||||
}
|
||||
};
|
||||
|
||||
export const postMessageToAdmins = async (messageType: AdminNotificationType) => {
|
||||
const body = `{"message_type": "${messageType}"}`;
|
||||
try {
|
||||
|
||||
@@ -4,14 +4,14 @@ import {useSelector} from 'react-redux';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import GenericModal, {DefaultFooterContainer} from 'src/components/widgets/generic_modal';
|
||||
import {postMessageToAdmins, requestTrialLicense} from 'src/client';
|
||||
import {postMessageToAdmins} from 'src/client';
|
||||
import UpgradeModalFooter from 'src/components/backstage/upgrade_modal_footer';
|
||||
|
||||
import {getAdminAnalytics, isCurrentUserAdmin, isTeamEdition} from 'src/selectors';
|
||||
|
||||
import {AdminNotificationType} from 'src/constants';
|
||||
import {isCloud} from 'src/license';
|
||||
import {useOpenCloudModal} from 'src/hooks';
|
||||
import {useOpenCloudModal, useOpenStartTrialFormModal} from 'src/hooks';
|
||||
|
||||
import {ModalActionState, getUpgradeModalButtons, getUpgradeModalCopy} from 'src/components/backstage/upgrade_modal_data';
|
||||
|
||||
@@ -29,6 +29,7 @@ const UpgradeModal = (props: Props) => {
|
||||
const isServerCloud = useSelector(isCloud);
|
||||
const isAdmin = useSelector(isCurrentUserAdmin);
|
||||
const isServerTeamEdition = useSelector(isTeamEdition);
|
||||
const openTrialFormModal = useOpenStartTrialFormModal();
|
||||
|
||||
const [actionState, setActionState] = useState(ModalActionState.Uninitialized);
|
||||
|
||||
@@ -40,14 +41,7 @@ const UpgradeModal = (props: Props) => {
|
||||
return;
|
||||
}
|
||||
setActionState(ModalActionState.Loading);
|
||||
|
||||
const requestedUsers = Math.max(serverTotalUsers, 30);
|
||||
const response = await requestTrialLicense(requestedUsers, props.messageType);
|
||||
if (response.error) {
|
||||
setActionState(ModalActionState.Error);
|
||||
} else {
|
||||
setActionState(ModalActionState.Success);
|
||||
}
|
||||
openTrialFormModal('playbooks_upgrade_modal');
|
||||
};
|
||||
|
||||
const openUpgradeModal = async () => {
|
||||
|
||||
@@ -14,10 +14,10 @@ import LoadingSpinner from 'src/components/assets/loading_spinner';
|
||||
import {getAdminAnalytics, isTeamEdition} from 'src/selectors';
|
||||
import StartTrialNotice from 'src/components/backstage/start_trial_notice';
|
||||
import ConvertEnterpriseNotice from 'src/components/backstage/convert_enterprise_notice';
|
||||
import {postMessageToAdmins, requestTrialLicense} from 'src/client';
|
||||
import {postMessageToAdmins} from 'src/client';
|
||||
import {AdminNotificationType} from 'src/constants';
|
||||
import {isCloud} from 'src/license';
|
||||
import {useOpenCloudModal} from 'src/hooks';
|
||||
import {useOpenCloudModal, useOpenStartTrialFormModal} from 'src/hooks';
|
||||
|
||||
import SuccessSvg from './assets/success_svg';
|
||||
import ErrorSvg from './assets/error_svg';
|
||||
@@ -125,6 +125,7 @@ const UpgradeBanner = (props: Props) => {
|
||||
const isCurrentUserAdmin = isSystemAdmin(currentUser.roles);
|
||||
const [actionState, setActionState] = useState(ActionState.Uninitialized);
|
||||
const isServerTeamEdition = useSelector(isTeamEdition);
|
||||
const openTrialFormModal = useOpenStartTrialFormModal();
|
||||
|
||||
const analytics = useSelector(getAdminAnalytics);
|
||||
const serverTotalUsers = analytics?.TOTAL_USERS || 0;
|
||||
@@ -149,15 +150,7 @@ const UpgradeBanner = (props: Props) => {
|
||||
return;
|
||||
}
|
||||
|
||||
setActionState(ActionState.Loading);
|
||||
|
||||
const requestedUsers = Math.max(serverTotalUsers, 30);
|
||||
const response = await requestTrialLicense(requestedUsers, props.notificationType);
|
||||
if (response.error) {
|
||||
setActionState(ActionState.Error);
|
||||
} else {
|
||||
setActionState(ActionState.Success);
|
||||
}
|
||||
openTrialFormModal('playbooks_upgrade_banner');
|
||||
};
|
||||
|
||||
const openUpgradeModal = async () => {
|
||||
|
||||
@@ -377,6 +377,39 @@ export function useEnsureProfiles(userIds: string[]) {
|
||||
}, [userIds]);
|
||||
}
|
||||
|
||||
export function useOpenStartTrialFormModal() {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
// @ts-ignore
|
||||
if (!window.WebappUtils?.modals?.openModal || !window.WebappUtils?.modals?.ModalIdentifiers?.START_TRIAL_FORM_MODAL || !window.Components?.StartTrialFormModal) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('unable to open cloud modal');
|
||||
|
||||
return () => {
|
||||
/*do nothing*/
|
||||
};
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const {openModal, ModalIdentifiers} = window.WebappUtils.modals;
|
||||
|
||||
// @ts-ignore
|
||||
const TrialModal = window.Components.StartTrialFormModal;
|
||||
|
||||
return (page?: string, onClose?: () => void) => {
|
||||
dispatch(
|
||||
openModal({
|
||||
modalId: ModalIdentifiers.START_TRIAL_FORM_MODAL,
|
||||
dialogType: TrialModal,
|
||||
dialogProps: {
|
||||
page,
|
||||
onClose,
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export function useOpenCloudModal() {
|
||||
const dispatch = useDispatch();
|
||||
const isServerCloud = useSelector(isCloud);
|
||||
|
||||
Ссылка в новой задаче
Block a user