Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "What brings you to FF?" to sign-up form #2378

Merged
merged 8 commits into from
Jul 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions frontend/src/api/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ const logout = () => {

const registerUser = async (options) => {
return client.post('/account/register', options).then((res) => {
product.identify(options.username, {
const person = {
name: options.name,
username: options.username,
email: options.email,
'ff-cloud-user': true,
'ff-cloud-joined': (new Date()).toUTCString()
})
'ff-cloud-joined': (new Date()).toUTCString(),
'join-reason': options.join_reason
}
product.identify(options.username, person)
product.capture('$ff-user-registered')
return res.data
})
Expand Down
27 changes: 21 additions & 6 deletions frontend/src/pages/account/Create.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,24 @@
v-html="settings['branding:account:signUpTopBanner']"></p>
<div>
<label>Username</label>
<ff-text-input ref="signup-username" label="username" :error="errors.username" v-model="input.username" />
<ff-text-input ref="signup-username" data-form="signup-username" label="username" :error="errors.username" v-model="input.username" />
<span class="ff-error-inline">{{ errors.username }}</span>
<label>Full Name</label>
<ff-text-input ref="signup-fullname" label="Full Name" :error="errors.name" v-model="input.name" />
<ff-text-input ref="signup-fullname" data-form="signup-fullname" label="Full Name" :error="errors.name" v-model="input.name" />
<span class="ff-error-inline">{{ errors.name }}</span>
<label>E-Mail Address</label>
<ff-text-input ref="signup-email" label="E-Mail Address" :error="errors.email" v-model="input.email" />
<ff-text-input ref="signup-email" data-form="signup-email" label="E-Mail Address" :error="errors.email" v-model="input.email" />
<span class="ff-error-inline">{{ errors.email }}</span>
<label>Password</label>
<ff-text-input ref="signup-password" label="password" :error="errors.password" v-model="input.password" type="password"/>
<ff-text-input ref="signup-password" data-form="signup-password" label="password" :error="errors.password" v-model="input.password" type="password"/>
<span class="ff-error-inline">{{ errors.password }}</span>
</div>
<div class="pt-3" v-if="askJoinReason">
<ff-radio-group label="What brings you to FlowForge?" v-model="input.join_reason" orientation="grid"
data-form="signup-join-reason" :options="reasons" />
</div>
<div v-if="settings['user:tcs-required']">
<ff-checkbox v-model="input.tcs_accepted">
<ff-checkbox v-model="input.tcs_accepted" data-form="signup-accept-tcs">
I accept the <a target="_blank" :href="settings['user:tcs-url']">FlowForge Terms &amp; Conditions.</a>
</ff-checkbox>
</div>
Expand Down Expand Up @@ -70,13 +74,20 @@ export default {
username: '',
email: '',
password: '',
join_reason: null,
tcs_accepted: false,
code: ''
},
errors: {
email: '',
password: ''
}
},
reasons: [
{ label: 'Business Needs', value: 'business' },
{ label: 'Personal Use', value: 'personal' },
{ label: 'Educational Use', value: 'education' },
{ label: 'Other', value: 'other' }
]
}
},
mounted () {
Expand All @@ -91,8 +102,12 @@ export default {
return (this.input.email && !this.errors.email) &&
(this.input.username && !this.errors.username) &&
this.input.password.length >= 8 &&
(this.input.join_reason) &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line breaks sign-up for instances without posthog, as join_reason is never set to anything so the sign-up button is never enabled.

(this.settings['user:tcs-required'] ? this.input.tcs_accepted : true) &&
(!this.errors.name)
},
askJoinReason () {
return !!window.posthog
}
},
watch: {
Expand Down
20 changes: 20 additions & 0 deletions frontend/src/stylesheets/layouts.scss
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ $nav_height: 60px;
margin-bottom: 4px;
font-size: 0.75rem;
}
.ff-radio-btn label {
color: $ff-grey-100;
font-weight: normal;
}
p {
margin-bottom: 1rem;
}
Expand All @@ -115,6 +119,22 @@ $nav_height: 60px;
border-color: $ff-teal-400;
}
}
.ff-radio-btn {
.checkbox {
border: 1px solid $ff-grey-600;
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='%23374151' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='4'/%3e%3c/svg%3e")
}
}

.checkbox {
&[checked=true] {
background-color: $ff-white;
border-color: $ff-grey-700;
}
&[checked=true]:after {
display: block;
}
}
.ff-error-inline {
margin-bottom: 12px;
}
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"@fastify/passport": "^2.3.0",
"@fastify/static": "^6.10.2",
"@fastify/websocket": "^8.1.0",
"@flowforge/forge-ui-components": "^0.6.3",
"@flowforge/forge-ui-components": "^0.6.4",
"@flowforge/localfs": "^1.8.0",
"@headlessui/vue": "1.7.14",
"@heroicons/vue": "1.0.6",
Expand Down
128 changes: 128 additions & 0 deletions test/e2e/frontend/cypress/tests/registration.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
describe('FlowForge - Sign Up Page', () => {
it('should present the relevant default fields', () => {
cy.intercept('GET', '/api/*/settings').as('getUserSettings')
cy.visit('/account/create')
cy.wait('@getUserSettings')

cy.get('[data-form="signup-username"]').should('be.visible')
cy.get('[data-form="signup-fullname"]').should('be.visible')
cy.get('[data-form="signup-email"]').should('be.visible')
cy.get('[data-form="signup-password"]').should('be.visible')

cy.get('[data-form="signup-join-reason"]').should('not.exist')
cy.get('[data-form="signup-accept-tcs"]').should('not.exist')
})

it('should present the option to accept the T&Cs if user settings are set accordingly', () => {
const EXAMPLE_TCS_URL = 'https://example.com/terms-and-conditions'

cy.intercept('GET', '/api/*/settings', (req) => {
req.continue((res) => {
res.body['user:tcs-required'] = true
res.body['user:tcs-url'] = EXAMPLE_TCS_URL
})
}).as('getUserSettings')
cy.visit('/account/create')
cy.wait('@getUserSettings')

cy.get('[data-form="signup-accept-tcs"]').should('be.visible')
cy.get('[data-form="signup-accept-tcs"] a').should('have.attr', 'href', EXAMPLE_TCS_URL)
})

it('should present the "What brings you to FlowForge" question if configured', () => {
Cypress.on('window:before:load', win => {
win.posthog = {
onFeatureFlags: () => {},
capture: () => {},
identify: () => {}
}
})

cy.intercept('GET', '/api/*/settings').as('getUserSettings')
cy.visit('/account/create')
cy.wait('@getUserSettings')

cy.get('[data-form="signup-join-reason"]').should('be.visible')
})

// it('requires a password', () => {
// cy.visit('/')
// cy.get('div[label=username] input').should('be.visible')
// cy.get('div[label=password] input').should('not.exist')
// // fill out username
// cy.get('div[label=username] input').type('wrongusername')
// // click "login"
// cy.get('[data-action="login"]').click()
// // should prompt for password as well
// cy.get('div[label=username] input').should('be.visible')
// cy.get('div[label=password] input').should('be.visible')
// // click "login"
// cy.get('[data-action="login"]').click()
// // should report password is required
// cy.get('div[label=password]').should('have.class', 'ff-input--error')
// cy.get('[data-el="errors-password"]').should('be.visible')
// // check where we are
// cy.url().should('not.include', '/applications')
// })

// it('prevents a user logging in with incorrect credentials', () => {
// cy.visit('/')
// cy.get('div[label=username] input').should('be.visible')
// cy.get('div[label=password] input').should('not.exist')
// // fill out username
// cy.get('div[label=username] input').type('wrongusername')
// // click "login"
// cy.get('[data-action="login"]').click()
// // should prompt for password as well
// cy.get('div[label=username] input').should('be.visible')
// cy.get('div[label=password] input').should('be.visible')
// // fill out username
// cy.get('div[label=password] input').type('wrongpassword')
// // click "login"
// cy.get('[data-action="login"]').click()
// // display "Login Failed"
// cy.get('[data-el="errors-general"]').should('be.visible')
// // check where we are
// cy.url().should('not.include', '/applications')
// })

// it('allows a user to login', () => {
// cy.visit('/')
// cy.get('div[label=username] input').should('be.visible')
// cy.get('div[label=password] input').should('not.exist')
// // fill out username
// cy.get('div[label=username] input').type('alice')
// // click "login"
// cy.get('[data-action="login"]').click()
// // should prompt for password as well
// cy.get('div[label=username] input').should('be.visible')
// cy.get('div[label=password] input').should('be.visible')
// // fill out pasword
// cy.get('div[label=password] input').type('aaPassword')
// // click "login"
// cy.get('[data-action="login"]').click()
// // check where we are
// cy.url().should('include', '/applications')
// })
// it('prevent long password', () => {
// cy.visit('')
// // fill out credentials
// cy.get('div[label=username] input').type('alice')
// cy.get('[data-action="login"]').click()
// // should prompt for password as well
// cy.get('div[label=username] input').should('be.visible')
// cy.get('div[label=password] input').should('be.visible')
// let passwd = ''
// for (let i = 0; i < 1030; i++) {
// passwd += 'x'
// }
// cy.get('div[label=password] input').type(passwd, { delay: 0.1 })
// // click "login"
// cy.get('[data-action="login"]').click()
// // should have error
// cy.get('div[label=password]').should('have.class', 'ff-input--error')
// cy.get('[data-el="errors-password"]').should('be.visible')
// // check where we are
// cy.url().should('not.include', '/applications')
// })
})