diff --git a/frontend/common/services/useEnvironment.ts b/frontend/common/services/useEnvironment.ts index 4a0c76e67f63..40d71733220a 100644 --- a/frontend/common/services/useEnvironment.ts +++ b/frontend/common/services/useEnvironment.ts @@ -6,6 +6,17 @@ export const environmentService = service .enhanceEndpoints({ addTagTypes: ['Environment'] }) .injectEndpoints({ endpoints: (builder) => ({ + createEnvironment: builder.mutation< + Res['environment'], + Req['createEnvironment'] + >({ + invalidatesTags: [{ id: 'LIST', type: 'Environment' }], + query: (body: Req['createEnvironment']) => ({ + body, + method: 'POST', + url: `environments/`, + }), + }), getEnvironment: builder.query({ providesTags: (res) => [{ id: res?.id, type: 'Environment' }], query: (query: Req['getEnvironment']) => ({ @@ -84,6 +95,7 @@ export async function updateEnvironment( // END OF FUNCTION_EXPORTS export const { + useCreateEnvironmentMutation, useGetEnvironmentMetricsQuery, useGetEnvironmentQuery, useGetEnvironmentsQuery, diff --git a/frontend/common/services/useOrganisation.ts b/frontend/common/services/useOrganisation.ts index 82e0f45f79f1..dfb1801108bc 100644 --- a/frontend/common/services/useOrganisation.ts +++ b/frontend/common/services/useOrganisation.ts @@ -6,6 +6,17 @@ export const organisationService = service .enhanceEndpoints({ addTagTypes: ['Organisation'] }) .injectEndpoints({ endpoints: (builder) => ({ + createOrganisation: builder.mutation< + Res['organisation'], + Req['createOrganisation'] + >({ + invalidatesTags: [{ id: 'LIST', type: 'Organisation' }], + query: (body: Req['createOrganisation']) => ({ + body, + method: 'POST', + url: `organisations/`, + }), + }), deleteOrganisation: builder.mutation({ invalidatesTags: [{ id: 'LIST', type: 'Organisation' }], query: ({ id }: Req['deleteOrganisation']) => ({ @@ -85,6 +96,7 @@ export async function getOrganisations( // END OF FUNCTION_EXPORTS export const { + useCreateOrganisationMutation, useDeleteOrganisationMutation, useGetOrganisationQuery, useGetOrganisationsQuery, diff --git a/frontend/common/services/useProject.ts b/frontend/common/services/useProject.ts index 73e4a65c0817..988d7607d6ed 100644 --- a/frontend/common/services/useProject.ts +++ b/frontend/common/services/useProject.ts @@ -7,6 +7,14 @@ export const projectService = service .enhanceEndpoints({ addTagTypes: ['Project'] }) .injectEndpoints({ endpoints: (builder) => ({ + createProject: builder.mutation({ + invalidatesTags: [{ id: 'LIST', type: 'Project' }], + query: (body: Req['createProject']) => ({ + body, + method: 'POST', + url: `projects/`, + }), + }), deleteProject: builder.mutation({ invalidatesTags: [{ id: 'LIST', type: 'Project' }], query: ({ id }: Req['deleteProject']) => ({ @@ -99,6 +107,7 @@ export async function getProject( // END OF FUNCTION_EXPORTS export const { + useCreateProjectMutation, useDeleteProjectMutation, useGetProjectPermissionsQuery, useGetProjectQuery, diff --git a/frontend/common/types/requests.ts b/frontend/common/types/requests.ts index 0677227f4702..b7b8c0f92fd5 100644 --- a/frontend/common/types/requests.ts +++ b/frontend/common/types/requests.ts @@ -172,6 +172,7 @@ export type Req = { }> getOrganisations: {} getOrganisation: { id: number } + createOrganisation: { name: string } updateOrganisation: { id: number; body: UpdateOrganisationBody } deleteOrganisation: { id: number } uploadOrganisationLicence: { @@ -635,6 +636,7 @@ export type Req = { id: string } getProject: { id: number } + createProject: { name: string; organisation: number } updateProject: { id: number; body: UpdateProjectBody } deleteProject: { id: number } migrateProject: { id: number } @@ -682,6 +684,7 @@ export type Req = { feature_id: number group_ids: number[] } + createEnvironment: { name: string; project: number } updateEnvironment: { id: number; body: Environment } createCloneIdentityFeatureStates: { environment_id: string diff --git a/frontend/web/components/App.js b/frontend/web/components/App.js index eba77aab7ab1..e2a584ac5387 100644 --- a/frontend/web/components/App.js +++ b/frontend/web/components/App.js @@ -136,8 +136,15 @@ const App = class extends Component { } if (!AccountStore.getOrganisation() && !invite) { - // If user has no organisation redirect to /create - this.props.history.replace(`/create${query}`) + // If user has no organisation redirect to /create — unless the new + // onboarding flow is enabled, which creates the organisation as its + // first step at /getting-started. + const noOrgDestination = Utils.getFlagsmithHasFeature( + 'onboarding_quickstart_flow', + ) + ? '/getting-started' + : '/create' + this.props.history.replace(`${noOrgDestination}${query}`) return } @@ -225,6 +232,12 @@ const App = class extends Component { const projectId = this.getProjectId(this.props) const environmentId = this.getEnvironmentId(this.props) + // The quickstart onboarding flow is a focused, distraction-free surface — + // suppress the marketing announcement banners while the user is in it. + const isOnboardingFlow = + pathname === '/getting-started' && + Utils.getFlagsmithHasFeature('onboarding_quickstart_flow') + if ( AccountStore.getOrganisation() && AccountStore.getOrganisation().block_access_to_admin && @@ -273,24 +286,36 @@ const App = class extends Component { onLogin={this.onLogin} > {({ isSaving, user }, { twoFactorLogin }) => { - return user && user.twoFactorPrompt ? ( -
- { - this.setState({ error: false }) - twoFactorLogin(this.state.pin, () => { - this.setState({ error: true }) - }) - }} - isLoading={isSaving} - onChange={(e) => - this.setState({ pin: Utils.safeParseEventValue(e) }) - } - /> -
- ) : ( + if (user && user.twoFactorPrompt) { + return ( +
+ { + this.setState({ error: false }) + twoFactorLogin(this.state.pin, () => { + this.setState({ error: true }) + }) + }} + isLoading={isSaving} + onChange={(e) => + this.setState({ pin: Utils.safeParseEventValue(e) }) + } + /> +
+ ) + } + + // Chromeless onboarding: render only the flow — no nav, sidebar, + // or header links — so the customer can't navigate away mid-flow. + // The flow provides its own explicit "Skip — set up manually" + // escape. + if (isOnboardingFlow) { + return
{this.props.children}
+ } + + return (