Skip to content

Commit 464c217

Browse files
authored
feat: hosting preferences (#5806)
1 parent 5598da0 commit 464c217

File tree

6 files changed

+223
-109
lines changed

6 files changed

+223
-109
lines changed

frontend/common/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,7 @@ const Constants = {
634634
'AUDIT_LOG': 'Audit Log Page',
635635
'COMING_SOON': 'Coming Soon Page',
636636
'CREATE_ENVIRONMENT': 'Create Environment Page',
637+
'CREATE_ORGANISATION': 'Create Organisation Page',
637638
'DOCUMENTATION': 'Documentation Page',
638639
'ENVIRONMENT_SETTINGS': 'Environment Settings Page',
639640
'FEATURES': 'Features Page',

frontend/common/types/responses.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ export type GettingStartedTask = {
242242
completed_at?: string
243243
}
244244
export type Onboarding = {
245+
hosting_preferences: ('public_saas' | 'private_saas' | 'self_hosted')[]
245246
tools: {
246247
completed: boolean
247248
integrations: string[]

frontend/web/components/base/forms/Checkbox.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,8 @@ const Checkbox: React.FC<CheckboxProps> = ({
2626
<input id={idRef.current} type='checkbox' checked={checked} />
2727
<label
2828
onClick={handleChange}
29-
className='mb-0'
29+
className='mb-0 user-select-none d-inline'
3030
htmlFor={idRef.current}
31-
style={{ display: 'inline' }}
3231
>
3332
<span className='checkbox mr-2'>
3433
{checked && <Icon name='checkmark-square' />}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React, { useState } from 'react'
2+
import Checkbox from './Checkbox'
3+
4+
interface CheckboxGroupItem {
5+
label: string
6+
value: string
7+
}
8+
9+
interface CheckboxGroupProps {
10+
items: CheckboxGroupItem[]
11+
selectedValues: string[]
12+
onChange: (selected: string[]) => void
13+
}
14+
15+
const CheckboxGroup: React.FC<CheckboxGroupProps> = ({
16+
items,
17+
onChange,
18+
selectedValues,
19+
}) => {
20+
const handleCheckboxChange = (value: string, checked: boolean) => {
21+
const newSelectedValues = checked
22+
? [...selectedValues, value]
23+
: selectedValues.filter((v) => v !== value)
24+
25+
onChange(newSelectedValues)
26+
}
27+
28+
return (
29+
<div className='d-flex flex-column gap-3 mt-1'>
30+
{items.map(({ label, value }) => (
31+
<Checkbox
32+
key={value}
33+
label={label}
34+
checked={selectedValues.includes(value)}
35+
onChange={(checked) => handleCheckboxChange(value, checked)}
36+
/>
37+
))}
38+
</div>
39+
)
40+
}
41+
42+
export default CheckboxGroup

frontend/web/components/pages/CreateOrganisationPage.js

Lines changed: 0 additions & 107 deletions
This file was deleted.
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
import React, { useEffect, useRef, useState } from 'react'
2+
import { useHistory } from 'react-router-dom'
3+
import ConfigProvider from 'common/providers/ConfigProvider'
4+
import Constants from 'common/constants'
5+
import PageTitle from 'components/PageTitle'
6+
import CondensedRow from 'components/CondensedRow'
7+
import InputGroup from 'components/base/forms/InputGroup'
8+
import Button from 'components/base/forms/Button'
9+
import API from 'project/api'
10+
import AppActions from 'common/dispatcher/app-actions'
11+
import Utils from 'common/utils/utils'
12+
// @ts-ignore
13+
import Project from 'common/project'
14+
import AccountStore from 'common/stores/account-store'
15+
import { Account } from 'common/types/responses'
16+
import CheckboxGroup from 'components/base/forms/CheckboxGroup'
17+
import OrganisationStore from 'common/stores/organisation-store'
18+
import { useUpdateOnboardingMutation } from 'common/services/useOnboarding'
19+
20+
const CreateOrganisationPage: React.FC = () => {
21+
const [name, setName] = useState<string>('')
22+
const inputRef = useRef<HTMLInputElement | null>(null)
23+
const focusTimeout = useRef<ReturnType<typeof setTimeout> | null>(null)
24+
const history = useHistory()
25+
const [hosting, setHosting] = useState(['public_saas'])
26+
const [accountStoreSaving, setAccountStoreSaving] = useState(
27+
AccountStore.isSaving,
28+
)
29+
const [updateTools] = useUpdateOnboardingMutation({})
30+
31+
useEffect(() => {
32+
const onChangeAccountStore = () => {
33+
setAccountStoreSaving(AccountStore.isSaving)
34+
}
35+
const onSave = () => {
36+
//@ts-ignore
37+
const id = AccountStore.savedId
38+
AppActions.selectOrganisation(id)
39+
API.setCookie('organisation', `${id}`)
40+
41+
if (Utils.isSaas()) {
42+
updateTools({
43+
hosting_preferences: hosting,
44+
})
45+
}
46+
47+
if (Utils.getFlagsmithHasFeature('welcome_page')) {
48+
history.push('/getting-started')
49+
} else {
50+
history.push(Utils.getOrganisationHomePage(id))
51+
}
52+
}
53+
AccountStore.on('change', onChangeAccountStore)
54+
AccountStore.on('saved', onSave)
55+
return () => {
56+
OrganisationStore.off('change', onChangeAccountStore)
57+
OrganisationStore.off('saved', onSave)
58+
}
59+
}, [hosting, history])
60+
61+
useEffect(() => {
62+
API.trackPage(Constants.pages.CREATE_ORGANISATION)
63+
focusTimeout.current = setTimeout(() => {
64+
inputRef.current?.focus()
65+
focusTimeout.current = null
66+
}, 500)
67+
68+
return () => {
69+
if (focusTimeout.current) {
70+
clearTimeout(focusTimeout.current)
71+
}
72+
}
73+
}, [])
74+
75+
if (
76+
Project.superUserCreateOnly &&
77+
// @ts-ignore
78+
!(AccountStore.model as Account)?.is_superuser
79+
) {
80+
return (
81+
<div className='text-center alert'>
82+
Your Flagsmith instance is setup to only allow super users to create an
83+
organisation, please contact your administrator.
84+
</div>
85+
)
86+
}
87+
88+
if (Utils.getFlagsmithHasFeature('disable_create_org')) {
89+
return (
90+
<div id='create-org-page' className='container app-container'>
91+
This Flagsmith instance is configured to prevent additional
92+
organisations from being created. Please contact an administrator. If
93+
you think you are seeing this page by mistake, please check you are
94+
invited to the correct organisation.
95+
</div>
96+
)
97+
}
98+
99+
return (
100+
<div id='create-org-page' className='container app-container'>
101+
<PageTitle title='Create your organisation'>
102+
Organisations allow you to manage multiple projects within a team.
103+
</PageTitle>
104+
<form
105+
onSubmit={(e) => {
106+
e.preventDefault()
107+
if (Project.capterraKey) {
108+
const parts = Project.capterraKey.split(',')
109+
Utils.appendImage(
110+
`https://ct.capterra.com/capterra_tracker.gif?vid=${parts[0]}&vkey=${parts[1]}`,
111+
)
112+
}
113+
AppActions.createOrganisation(name)
114+
}}
115+
>
116+
<CondensedRow>
117+
<InputGroup
118+
ref={inputRef as any}
119+
inputProps={{ className: 'full-width', name: 'orgName' }}
120+
title='Organisation Name'
121+
placeholder='E.g. ACME Ltd'
122+
onChange={(e: InputEvent) => setName(Utils.safeParseEventValue(e))}
123+
/>
124+
{Utils.isSaas() && (
125+
<InputGroup
126+
inputProps={{ className: 'full-width', name: 'orgName' }}
127+
title={
128+
<div>
129+
What is your company's desired hosting option?{' '}
130+
<a
131+
className='text-primary'
132+
href='https://docs.flagsmith.com/version-comparison'
133+
target='_blank'
134+
rel='noreferrer'
135+
>
136+
View Docs
137+
</a>
138+
</div>
139+
}
140+
component={
141+
<CheckboxGroup
142+
onChange={setHosting}
143+
selectedValues={hosting}
144+
items={[
145+
{
146+
label: 'Public SaaS (Multi Tenant)',
147+
value: 'public_saas',
148+
},
149+
{
150+
label: 'Private SaaS (Single Tenant)',
151+
value: 'private_saas',
152+
},
153+
{
154+
label: 'Self Hosted (in your own cloud)',
155+
value: 'self_hosted',
156+
},
157+
]}
158+
/>
159+
}
160+
/>
161+
)}
162+
163+
<div className='text-right'>
164+
<Button
165+
type='submit'
166+
disabled={accountStoreSaving || !name}
167+
id='create-org-btn'
168+
>
169+
Create Organisation
170+
</Button>
171+
</div>
172+
</CondensedRow>
173+
</form>
174+
</div>
175+
)
176+
}
177+
178+
export default ConfigProvider(CreateOrganisationPage)

0 commit comments

Comments
 (0)