web, satellite: allow registering business accounts to ask for contact from sales team

Full prefix: web/satellite, satellite/{console, analytics, satellitedb}

- checkbox added to register view - business tab
- user being saved with new column
- add sales contact choice to Segment calls
- ui fix added to employee count dropdown

Change-Id: Ib976872463b88874ea9714db635d58c79cdbe3a1
This commit is contained in:
Malcolm Bouzi 2021-04-27 20:40:03 +02:00
parent 2cf10a7bf4
commit 136af8e630
11 changed files with 122 additions and 79 deletions

View File

@ -87,6 +87,7 @@ type TrackCreateUserFields struct {
EmployeeCount string EmployeeCount string
CompanyName string CompanyName string
JobTitle string JobTitle string
HaveSalesContact bool
} }
func (service *Service) enqueueMessage(message segment.Message) { func (service *Service) enqueueMessage(message segment.Message) {
@ -122,6 +123,7 @@ func (service *Service) TrackCreateUser(fields TrackCreateUserFields) {
props.Set("company_size", fields.EmployeeCount) props.Set("company_size", fields.EmployeeCount)
props.Set("company_name", fields.CompanyName) props.Set("company_name", fields.CompanyName)
props.Set("job_title", fields.JobTitle) props.Set("job_title", fields.JobTitle)
props.Set("have_sales_contact", fields.HaveSalesContact)
} }
service.enqueueMessage(segment.Track{ service.enqueueMessage(segment.Track{

View File

@ -124,6 +124,7 @@ func (a *Auth) Register(w http.ResponseWriter, r *http.Request) {
Position string `json:"position"` Position string `json:"position"`
CompanyName string `json:"companyName"` CompanyName string `json:"companyName"`
EmployeeCount string `json:"employeeCount"` EmployeeCount string `json:"employeeCount"`
HaveSalesContact bool `json:"haveSalesContact"`
} }
err = json.NewDecoder(r.Body).Decode(&registerData) err = json.NewDecoder(r.Body).Decode(&registerData)
@ -158,6 +159,7 @@ func (a *Auth) Register(w http.ResponseWriter, r *http.Request) {
Position: registerData.Position, Position: registerData.Position,
CompanyName: registerData.CompanyName, CompanyName: registerData.CompanyName,
EmployeeCount: registerData.EmployeeCount, EmployeeCount: registerData.EmployeeCount,
HaveSalesContact: registerData.HaveSalesContact,
}, },
secret, secret,
) )
@ -178,6 +180,7 @@ func (a *Auth) Register(w http.ResponseWriter, r *http.Request) {
trackCreateUserFields.EmployeeCount = user.EmployeeCount trackCreateUserFields.EmployeeCount = user.EmployeeCount
trackCreateUserFields.CompanyName = user.CompanyName trackCreateUserFields.CompanyName = user.CompanyName
trackCreateUserFields.JobTitle = user.Position trackCreateUserFields.JobTitle = user.Position
trackCreateUserFields.HaveSalesContact = user.HaveSalesContact
} }
a.analytics.TrackCreateUser(trackCreateUserFields) a.analytics.TrackCreateUser(trackCreateUserFields)
@ -260,6 +263,7 @@ func (a *Auth) GetAccount(w http.ResponseWriter, r *http.Request) {
Position string `json:"position"` Position string `json:"position"`
CompanyName string `json:"companyName"` CompanyName string `json:"companyName"`
EmployeeCount string `json:"employeeCount"` EmployeeCount string `json:"employeeCount"`
HaveSalesContact bool `json:"haveSalesContact"`
} }
auth, err := console.GetAuth(ctx) auth, err := console.GetAuth(ctx)
@ -278,6 +282,7 @@ func (a *Auth) GetAccount(w http.ResponseWriter, r *http.Request) {
user.CompanyName = auth.User.CompanyName user.CompanyName = auth.User.CompanyName
user.Position = auth.User.Position user.Position = auth.User.Position
user.EmployeeCount = auth.User.EmployeeCount user.EmployeeCount = auth.User.EmployeeCount
user.HaveSalesContact = auth.User.HaveSalesContact
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(&user) err = json.NewEncoder(w).Encode(&user)

View File

@ -571,7 +571,9 @@ func (s *Service) CreateUser(ctx context.Context, user CreateUser, tokenSecret R
Position: user.Position, Position: user.Position,
CompanyName: user.CompanyName, CompanyName: user.CompanyName,
EmployeeCount: user.EmployeeCount, EmployeeCount: user.EmployeeCount,
HaveSalesContact: user.HaveSalesContact,
} }
if user.PartnerID != "" { if user.PartnerID != "" {
newUser.PartnerID, err = uuid.FromString(user.PartnerID) newUser.PartnerID, err = uuid.FromString(user.PartnerID)
if err != nil { if err != nil {

View File

@ -59,6 +59,7 @@ type CreateUser struct {
CompanyName string `json:"companyName"` CompanyName string `json:"companyName"`
WorkingOn string `json:"workingOn"` WorkingOn string `json:"workingOn"`
EmployeeCount string `json:"employeeCount"` EmployeeCount string `json:"employeeCount"`
HaveSalesContact bool `json:"haveSalesContact"`
} }
// IsValid checks CreateUser validity and returns error describing whats wrong. // IsValid checks CreateUser validity and returns error describing whats wrong.
@ -117,4 +118,6 @@ type User struct {
CompanySize int `json:"companySize"` CompanySize int `json:"companySize"`
WorkingOn string `json:"workingOn"` WorkingOn string `json:"workingOn"`
EmployeeCount string `json:"employeeCount"` EmployeeCount string `json:"employeeCount"`
HaveSalesContact bool `json:"haveSalesContact"`
} }

View File

@ -68,6 +68,7 @@ func (users *users) Insert(ctx context.Context, user *console.User) (_ *console.
optional.CompanyName = dbx.User_CompanyName(user.CompanyName) optional.CompanyName = dbx.User_CompanyName(user.CompanyName)
optional.WorkingOn = dbx.User_WorkingOn(user.WorkingOn) optional.WorkingOn = dbx.User_WorkingOn(user.WorkingOn)
optional.EmployeeCount = dbx.User_EmployeeCount(user.EmployeeCount) optional.EmployeeCount = dbx.User_EmployeeCount(user.EmployeeCount)
optional.HaveSalesContact = dbx.User_HaveSalesContact(user.HaveSalesContact)
} }
createdUser, err := users.db.Create_User(ctx, createdUser, err := users.db.Create_User(ctx,
@ -158,6 +159,7 @@ func userFromDBX(ctx context.Context, user *dbx.User) (_ *console.User, err erro
CreatedAt: user.CreatedAt, CreatedAt: user.CreatedAt,
ProjectLimit: user.ProjectLimit, ProjectLimit: user.ProjectLimit,
IsProfessional: user.IsProfessional, IsProfessional: user.IsProfessional,
HaveSalesContact: user.HaveSalesContact,
} }
if user.PartnerId != nil { if user.PartnerId != nil {

View File

@ -205,7 +205,7 @@ export class AuthHttpApi {
* @returns id of created user * @returns id of created user
* @throws Error * @throws Error
*/ */
public async register(user: {fullName: string; shortName: string; email: string; partner: string; partnerId: string; password: string; isProfessional: boolean; position: string; companyName: string; employeeCount: string}, secret: string): Promise<string> { public async register(user: {fullName: string; shortName: string; email: string; partner: string; partnerId: string; password: string; isProfessional: boolean; position: string; companyName: string; employeeCount: string; haveSalesContact: boolean }, secret: string): Promise<string> {
const path = `${this.ROOT_PATH}/register`; const path = `${this.ROOT_PATH}/register`;
const body = { const body = {
secret: secret, secret: secret,
@ -219,8 +219,8 @@ export class AuthHttpApi {
position: user.position, position: user.position,
companyName: user.companyName, companyName: user.companyName,
employeeCount: user.employeeCount, employeeCount: user.employeeCount,
haveSalesContact: user.haveSalesContact,
}; };
const response = await this.http.post(path, JSON.stringify(body)); const response = await this.http.post(path, JSON.stringify(body));
if (!response.ok) { if (!response.ok) {
switch (response.status) { switch (response.status) {

View File

@ -252,8 +252,8 @@ export default class HeaderlessInput extends Vue {
&__options-wrapper { &__options-wrapper {
border: 1px solid rgba(56, 75, 101, 0.4); border: 1px solid rgba(56, 75, 101, 0.4);
position: absolute; position: absolute;
width: calc(100% - 4px); width: calc(100% - 5px);
top: 86px; top: 70px;
padding: 0; padding: 0;
background: #fff; background: #fff;
z-index: 21; z-index: 21;

View File

@ -38,6 +38,7 @@ export class User {
public position: string = '', public position: string = '',
public companyName: string = '', public companyName: string = '',
public employeeCount: string = '', public employeeCount: string = '',
public haveSalesContact: boolean = false,
) {} ) {}
public getFullName(): string { public getFullName(): string {

View File

@ -65,6 +65,7 @@ export default class RegisterArea extends Vue {
private isTermsAcceptedError: boolean = false; private isTermsAcceptedError: boolean = false;
private isLoading: boolean = false; private isLoading: boolean = false;
private isProfessional: boolean = false; private isProfessional: boolean = false;
private haveSalesContact: boolean = false;
private readonly auth: AuthHttpApi = new AuthHttpApi(); private readonly auth: AuthHttpApi = new AuthHttpApi();
@ -328,6 +329,8 @@ export default class RegisterArea extends Vue {
*/ */
private async createUser(): Promise<void> { private async createUser(): Promise<void> {
this.user.isProfessional = this.isProfessional; this.user.isProfessional = this.isProfessional;
this.user.haveSalesContact = this.haveSalesContact;
try { try {
this.userId = await this.auth.register(this.user, this.secret); this.userId = await this.auth.register(this.user, this.secret);
LocalData.setUserId(this.userId); LocalData.setUserId(this.userId);

View File

@ -137,17 +137,28 @@
/> />
</div> </div>
<AddCouponCodeInput v-if="couponCodeUIEnabled" /> <AddCouponCodeInput v-if="couponCodeUIEnabled" />
<div class="register-area__content-area__container__terms-area"> <div v-if="isProfessional" class="register-area__content-area__container__checkbox-area">
<label class="container">
<input type="checkbox" v-model="haveSalesContact">
<span class="checkmark"></span>
</label>
<label class="register-area__content-area__container__checkbox-area__msg-box" for="terms">
<p class="register-area__content-area__container__checkbox-area__msg-box__msg">
Please have the Sales Team contact me
</p>
</label>
</div>
<div class="register-area__content-area__container__checkbox-area">
<label class="container"> <label class="container">
<input id="terms" type="checkbox" v-model="isTermsAccepted"> <input id="terms" type="checkbox" v-model="isTermsAccepted">
<span class="checkmark" :class="{'error': isTermsAcceptedError}"></span> <span class="checkmark" :class="{'error': isTermsAcceptedError}"></span>
</label> </label>
<label class="register-area__content-area__container__terms-area__msg-box" for="terms"> <label class="register-area__content-area__container__checkbox-area__msg-box" for="terms">
<p class="register-area__content-area__container__terms-area__msg-box__msg"> <p class="register-area__content-area__container__checkbox-area__msg-box__msg">
I agree to the I agree to the
<a class="register-area__content-area__container__terms-area__msg-box__msg__link" href="https://storj.io/terms-of-service/" target="_blank" rel="noopener">Terms of Service</a> <a class="register-area__content-area__container__checkbox-area__msg-box__msg__link" href="https://storj.io/terms-of-service/" target="_blank" rel="noopener">Terms of Service</a>
and and
<a class="register-area__content-area__container__terms-area__msg-box__msg__link" href="https://storj.io/privacy-policy/" target="_blank" rel="noopener">Privacy Policy</a> <a class="register-area__content-area__container__checkbox-area__msg-box__msg__link" href="https://storj.io/privacy-policy/" target="_blank" rel="noopener">Privacy Policy</a>
</p> </p>
</label> </label>
</div> </div>

View File

@ -118,6 +118,8 @@ h1 {
border-top-right-radius: 20px; border-top-right-radius: 20px;
border-bottom-right-radius: 20px; border-bottom-right-radius: 20px;
border-left: none; border-left: none;
position: relative;
right: 1px;
} }
&__personal, &__personal,
@ -145,7 +147,7 @@ h1 {
padding: 60px 80px; padding: 60px 80px;
background-color: #fff; background-color: #fff;
border-radius: 6px; border-radius: 6px;
min-height: 675px; min-height: 655px;
&__title-area { &__title-area {
display: flex; display: flex;
@ -169,7 +171,7 @@ h1 {
} }
} }
&__terms-area { &__checkbox-area {
display: flex; display: flex;
align-items: center; align-items: center;
width: 100%; width: 100%;
@ -181,6 +183,8 @@ h1 {
color: #354049; color: #354049;
&__msg { &__msg {
position: relative;
top: 4px;
&__link { &__link {
margin: 0 4px; margin: 0 4px;
@ -219,7 +223,7 @@ h1 {
} }
&__container.professional-container { &__container.professional-container {
min-height: 901px; min-height: 991px;
} }
&__footer { &__footer {
@ -377,6 +381,16 @@ h1 {
} }
} }
@media screen and (max-width: 414px) {
.register-area {
&__logo-wrapper {
margin-top: 30px;
}
}
}
@media screen and (max-width: 414px) { @media screen and (max-width: 414px) {
.register-area { .register-area {