Commit 2b289593 authored by Alex Buijs's avatar Alex Buijs

Add page to create a new project

When part of the onboarding issues experiment flow
parent 6b03b20e
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
**tracking_attrs(track_label, 'click_button', 'bitbucket_cloud') do **tracking_attrs(track_label, 'click_button', 'bitbucket_cloud') do
= icon('bitbucket', text: 'Bitbucket Cloud') = icon('bitbucket', text: 'Bitbucket Cloud')
- unless bitbucket_import_configured? - unless bitbucket_import_configured?
= render 'bitbucket_import_modal' = render 'projects/bitbucket_import_modal'
- if bitbucket_server_import_enabled? - if bitbucket_server_import_enabled?
%div %div
= link_to status_import_bitbucket_server_path, class: "btn import_bitbucket", **tracking_attrs(track_label, 'click_button', 'bitbucket_server') do = link_to status_import_bitbucket_server_path, class: "btn import_bitbucket", **tracking_attrs(track_label, 'click_button', 'bitbucket_server') do
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
**tracking_attrs(track_label, 'click_button', 'gitlab_com') do **tracking_attrs(track_label, 'click_button', 'gitlab_com') do
= icon('gitlab', text: 'GitLab.com') = icon('gitlab', text: 'GitLab.com')
- unless gitlab_import_configured? - unless gitlab_import_configured?
= render 'gitlab_import_modal' = render 'projects/gitlab_import_modal'
- if google_code_import_enabled? - if google_code_import_enabled?
%div %div
...@@ -73,4 +73,4 @@ ...@@ -73,4 +73,4 @@
= form_for @project, html: { class: 'new_project' } do |f| = form_for @project, html: { class: 'new_project' } do |f|
%hr %hr
= render "shared/import_form", f: f = render "shared/import_form", f: f
= render 'new_project_fields', f: f, project_name_id: "import-url-name", hide_init_with_readme: true, track_label: track_label = render 'projects/new_project_fields', f: f, project_name_id: "import-url-name", hide_init_with_readme: true, track_label: track_label
...@@ -52,6 +52,7 @@ Rails.application.routes.draw do ...@@ -52,6 +52,7 @@ Rails.application.routes.draw do
Gitlab.ee do Gitlab.ee do
resources :groups, only: [:new, :create] resources :groups, only: [:new, :create]
resources :projects, only: [:new, :create]
end end
end end
......
import mountProgressBar from 'ee/registrations/projects/new';
import initProjectVisibilitySelector from '~/project_visibility';
import initProjectNew from '~/projects/project_new';
document.addEventListener('DOMContentLoaded', () => {
mountProgressBar();
initProjectVisibilitySelector();
initProjectNew.bindEvents();
});
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import {
STEPS,
ONBOARDING_ISSUES_EXPERIMENT_FLOW_STEPS,
ONBOARDING_ISSUES_EXPERIMENT_AND_SUBSCRIPTION_FLOW_STEPS,
} from '../../constants';
import ProgressBar from '../../components/progress_bar.vue';
export default () => {
const el = document.getElementById('progress-bar');
if (!el) return null;
const isInSubscriptionFlow = parseBoolean(el.dataset.isInSubscriptionFlow);
const steps = isInSubscriptionFlow
? ONBOARDING_ISSUES_EXPERIMENT_AND_SUBSCRIPTION_FLOW_STEPS
: ONBOARDING_ISSUES_EXPERIMENT_FLOW_STEPS;
return new Vue({
el,
render(createElement) {
return createElement(ProgressBar, {
props: { steps, currentStep: STEPS.yourProject },
});
},
});
};
...@@ -156,7 +156,8 @@ $subscriptions-full-width-lg: 541px; ...@@ -156,7 +156,8 @@ $subscriptions-full-width-lg: 541px;
} }
.edit-group, .edit-group,
.edit-profile { .edit-profile,
.new-project {
max-width: 400px; max-width: 400px;
.bar { .bar {
...@@ -170,5 +171,19 @@ $subscriptions-full-width-lg: 541px; ...@@ -170,5 +171,19 @@ $subscriptions-full-width-lg: 541px;
.field_with_errors { .field_with_errors {
display: inline; display: inline;
} }
.project-import {
h5 {
display: none;
}
.import-buttons {
justify-content: center;
.btn {
width: 288px;
}
}
}
} }
} }
...@@ -5,6 +5,7 @@ module Registrations ...@@ -5,6 +5,7 @@ module Registrations
layout 'checkout' layout 'checkout'
before_action :authorize_create_group!, only: :new before_action :authorize_create_group!, only: :new
before_action :check_experiment_enabled
def new def new
@group = Group.new @group = Group.new
...@@ -14,7 +15,7 @@ module Registrations ...@@ -14,7 +15,7 @@ module Registrations
@group = Groups::CreateService.new(current_user, group_params).execute @group = Groups::CreateService.new(current_user, group_params).execute
if @group.persisted? if @group.persisted?
redirect_to @group redirect_to new_users_sign_up_project_path(namespace_id: @group.id)
else else
render action: :new render action: :new
end end
...@@ -26,6 +27,10 @@ module Registrations ...@@ -26,6 +27,10 @@ module Registrations
access_denied! unless can?(current_user, :create_group) access_denied! unless can?(current_user, :create_group)
end end
def check_experiment_enabled
access_denied! unless experiment_enabled?(:onboarding_issues)
end
def group_params def group_params
params.require(:group).permit(:name, :path, :visibility_level) params.require(:group).permit(:name, :path, :visibility_level)
end end
......
# frozen_string_literal: true
module Registrations
class ProjectsController < ApplicationController
layout 'checkout'
before_action :check_experiment_enabled
before_action :find_namespace, only: :new
def new
@project = Project.new(namespace: @namespace)
end
def create
@project = ::Projects::CreateService.new(current_user, project_params).execute
if @project.saved?
redirect_to project_path(@project)
else
render :new
end
end
private
def check_experiment_enabled
access_denied! unless experiment_enabled?(:onboarding_issues)
end
def find_namespace
@namespace = Namespace.find_by_id(params[:namespace_id])
access_denied! unless can?(current_user, :create_projects, @namespace)
end
def project_params
params.require(:project).permit(project_params_attributes)
end
def project_params_attributes
[
:namespace_id,
:name,
:path,
:visibility_level
]
end
end
end
- page_title _('Your first project')
- visibility_level = selected_visibility_level(@project, params.dig(:project, :visibility_level))
.row.flex-grow-1.bg-gray-light
.d-flex.flex-column.align-items-center.w-100.p-3
.new-project.d-flex.flex-column.align-items-center.pt-5
#progress-bar{ data: { is_in_subscription_flow: in_subscription_flow?.to_s } }
%h2.center= _('Create/import your first project')
%p
.center= _('This project will live in your group <strong>%{namespace}</strong>. A project is where you house your files (repository), plan your work (issues), publish your documentation (wiki), and so much more.').html_safe % { namespace: html_escape(@project.namespace.name) }
.js-toggle-container.w-100
%ul.nav.nav-tabs.nav-links.gitlab-tabs{ role: 'tablist' }
%li.nav-item{ role: 'presentation' }
%a#blank-project-tab.nav-link.active{ href: '#blank-project-pane', data: { toggle: 'tab', track_label: 'blank_project', track_event: 'click_tab', track_value: '' }, role: 'tab' }
%span= s_('ProjectsNew|Create')
%li.nav-item{ role: 'presentation' }
%a#import-project-tab.nav-link{ href: '#import-project-pane', data: { toggle: 'tab', track_label: 'import_project', track_event: 'click_tab', track_value: '' }, role: 'tab' }
%span= s_('ProjectsNew|Import')
.tab-content.gitlab-tab-content.bg-white
#blank-project-pane.tab-pane.js-toggle-container.active{ role: 'tabpanel' }
= form_for @project, url: users_sign_up_projects_path, html: { class: 'new_project' } do |f|
= form_errors(@project)
= f.hidden_field :namespace_id, value: @project.namespace_id
#blank-project-name.row
.form-group.project-name.col-sm-12
= f.label :name, class: 'label-bold' do
%span= _('Project name')
= f.text_field :name, class: 'form-control', autofocus: true, data: { track_label: 'blank_project', track_event: 'activate_form_input', track_property: 'project_name', track_value: '' }
.form-text.text-muted= _('You can always edit this later')
.form-group.col-sm-12
= f.label :path, class: 'label-bold' do
%span= _('Project URL')
.input-group.flex-nowrap
.input-group-prepend.flex-shrink-0.has-tooltip{ title: "#{group_url(@project.namespace)}/" }
.input-group-text
#{group_url(@project.namespace)}/
= f.text_field :path, class: 'form-control', required: true
= f.label :visibility_level, class: 'label-bold' do
= s_('ProjectsNew|Visibility Level')
= link_to icon('question-circle'), help_page_path('public_access/public_access'), aria: { label: 'Documentation for Visibility Level' }, target: '_blank', rel: 'noopener noreferrer'
= render 'shared/visibility_level', f: f, visibility_level: visibility_level.to_i, can_change_visibility_level: true, form_model: @project, with_label: false
= f.submit _('Create project'), class: 'btn btn-success project-submit w-100', data: { track_label: 'blank_project', track_event: 'click_button', track_property: 'create_project', track_value: '' }
#import-project-pane.tab-pane.import-project-pane.js-toggle-container{ role: 'tabpanel' }
- if import_sources_enabled?
= render 'projects/import_project_pane'
- else
.nothing-here-block
%h4= s_('ProjectsNew|No import options available')
%p= s_('ProjectsNew|Contact an administrator to enable options for importing your project.')
...@@ -16,10 +16,10 @@ describe Registrations::GroupsController do ...@@ -16,10 +16,10 @@ describe Registrations::GroupsController do
context 'with an authenticated user' do context 'with an authenticated user' do
before do before do
sign_in(user) sign_in(user)
stub_experiment_for_user(onboarding_issues: true)
end end
it { is_expected.to have_gitlab_http_status(:ok) } it { is_expected.to have_gitlab_http_status(:ok) }
it { is_expected.to render_template(:new) } it { is_expected.to render_template(:new) }
context 'user without the ability to create a group' do context 'user without the ability to create a group' do
...@@ -27,6 +27,14 @@ describe Registrations::GroupsController do ...@@ -27,6 +27,14 @@ describe Registrations::GroupsController do
it { is_expected.to have_gitlab_http_status(:not_found) } it { is_expected.to have_gitlab_http_status(:not_found) }
end end
context 'with the experiment not enabled for user' do
before do
stub_experiment_for_user(onboarding_issues: false)
end
it { is_expected.to have_gitlab_http_status(:not_found) }
end
end end
end end
...@@ -43,33 +51,34 @@ describe Registrations::GroupsController do ...@@ -43,33 +51,34 @@ describe Registrations::GroupsController do
context 'with an authenticated user' do context 'with an authenticated user' do
before do before do
sign_in(user) sign_in(user)
stub_experiment_for_user(onboarding_issues: true)
end end
it 'creates a group' do it 'creates a group' do
expect { subject }.to change { Group.count }.by(1) expect { subject }.to change { Group.count }.by(1)
end end
it 'redirects to the group path' do it { is_expected.to have_gitlab_http_status(:redirect) }
expect(subject).to have_gitlab_http_status(:redirect) it { is_expected.to redirect_to(new_users_sign_up_project_path(namespace_id: user.groups.last.id)) }
expect(subject).to redirect_to('/group-path')
end
end
context 'when the group cannot be saved' do context 'when the group cannot be saved' do
before do let(:params) { { name: '', path: '' } }
sign_in(user)
end
let(:params) { { name: '', path: '' } } it 'does not create a group' do
expect { subject }.not_to change { Group.count }
expect(assigns(:group).errors).not_to be_blank
end
it 'does not create a group' do it { is_expected.to have_gitlab_http_status(:ok) }
expect { subject }.not_to change { Group.count } it { is_expected.to render_template(:new) }
expect(assigns(:group).errors).not_to be_blank
end end
it 'renders the new template' do context 'with the experiment not enabled for user' do
expect(subject).to have_gitlab_http_status(:ok) before do
expect(subject).to render_template(:new) stub_experiment_for_user(onboarding_issues: false)
end
it { is_expected.to have_gitlab_http_status(:not_found) }
end end
end end
end end
......
# frozen_string_literal: true
require 'spec_helper'
describe Registrations::ProjectsController do
let_it_be(:user) { create(:user) }
let_it_be(:namespace) { create(:group, path: 'group-path') }
describe 'GET #new' do
subject { get :new }
context 'with an unauthenticated user' do
it { is_expected.to have_gitlab_http_status(:redirect) }
it { is_expected.to redirect_to(new_user_session_path) }
end
context 'with an authenticated user' do
before do
sign_in(user)
stub_experiment_for_user(onboarding_issues: true)
end
it { is_expected.to have_gitlab_http_status(:not_found) }
context 'with a namespace in the URL' do
subject { get :new, params: { namespace_id: namespace.id } }
it { is_expected.to have_gitlab_http_status(:not_found) }
context 'with sufficient access' do
before do
namespace.add_owner(user)
end
it { is_expected.to have_gitlab_http_status(:ok) }
it { is_expected.to render_template(:new) }
end
end
context 'with the experiment not enabled for user' do
before do
stub_experiment_for_user(onboarding_issues: false)
end
it { is_expected.to have_gitlab_http_status(:not_found) }
end
end
end
describe 'POST #create' do
subject { post :create, params: { project: params } }
let(:params) { { namespace_id: namespace.id, name: 'Project name', path: 'project-path', visibility_level: Gitlab::VisibilityLevel::PRIVATE } }
context 'with an unauthenticated user' do
it { is_expected.to have_gitlab_http_status(:redirect) }
it { is_expected.to redirect_to(new_user_session_path) }
end
context 'with an authenticated user' do
before do
namespace.add_owner(user)
sign_in(user)
stub_experiment_for_user(onboarding_issues: true)
end
it 'creates a project' do
expect { subject }.to change { Project.count }.by(1)
end
it { is_expected.to have_gitlab_http_status(:redirect) }
it { is_expected.to redirect_to('/group-path/project-path') }
context 'when the project cannot be saved' do
let(:params) { { name: '', path: '' } }
it 'does not create a project' do
expect { subject }.not_to change { Project.count }
end
it { is_expected.to have_gitlab_http_status(:ok) }
it { is_expected.to render_template(:new) }
end
context 'with the experiment not enabled for user' do
before do
stub_experiment_for_user(onboarding_issues: false)
end
it { is_expected.to have_gitlab_http_status(:not_found) }
end
end
end
end
...@@ -7,6 +7,7 @@ describe 'New group screen', :js do ...@@ -7,6 +7,7 @@ describe 'New group screen', :js do
before do before do
gitlab_sign_in(user) gitlab_sign_in(user)
stub_experiment_for_user(onboarding_issues: true)
visit new_users_sign_up_group_path visit new_users_sign_up_group_path
end end
...@@ -14,7 +15,6 @@ describe 'New group screen', :js do ...@@ -14,7 +15,6 @@ describe 'New group screen', :js do
it 'shows the progress bar with the correct steps' do it 'shows the progress bar with the correct steps' do
expect(subject).to have_content('Create your group') expect(subject).to have_content('Create your group')
expect(subject).to have_content('1. Your profile 2. Your GitLab group 3. Your first project') expect(subject).to have_content('1. Your profile 2. Your GitLab group 3. Your first project')
end end
end end
# frozen_string_literal: true
require 'spec_helper'
describe 'New project screen', :js do
let_it_be(:user) { create(:user) }
let_it_be(:namespace) { create(:group) }
let(:in_subscription_flow) { false }
before do
gitlab_sign_in(user)
namespace.add_owner(user)
allow_any_instance_of(EE::RegistrationsHelper).to receive(:in_subscription_flow?).and_return(in_subscription_flow)
stub_experiment_for_user(onboarding_issues: true)
visit new_users_sign_up_project_path(namespace_id: namespace.id)
end
subject { page }
it 'shows the progress bar with the correct steps' do
expect(subject).to have_content('Create/import your first project')
expect(subject).to have_content('1. Your profile 2. Your GitLab group 3. Your first project')
end
context 'when in the subscription flow' do
let(:in_subscription_flow) { true }
it { is_expected.to have_content('1. Your profile 2. Checkout 3. Your GitLab group 4. Your first project') }
end
end
...@@ -6483,6 +6483,9 @@ msgstr "" ...@@ -6483,6 +6483,9 @@ msgstr ""
msgid "Create your group" msgid "Create your group"
msgstr "" msgstr ""
msgid "Create/import your first project"
msgstr ""
msgid "CreateGroup|You don’t have permission to create a subgroup in this group." msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr "" msgstr ""
...@@ -17182,6 +17185,9 @@ msgstr "" ...@@ -17182,6 +17185,9 @@ msgstr ""
msgid "ProjectsNew|Contact an administrator to enable options for importing your project." msgid "ProjectsNew|Contact an administrator to enable options for importing your project."
msgstr "" msgstr ""
msgid "ProjectsNew|Create"
msgstr ""
msgid "ProjectsNew|Create from template" msgid "ProjectsNew|Create from template"
msgstr "" msgstr ""
...@@ -22466,6 +22472,9 @@ msgstr "" ...@@ -22466,6 +22472,9 @@ msgstr ""
msgid "This project will be removed on %{date} since its parent group '%{parent_group_name}' has been scheduled for removal." msgid "This project will be removed on %{date} since its parent group '%{parent_group_name}' has been scheduled for removal."
msgstr "" msgstr ""
msgid "This project will live in your group <strong>%{namespace}</strong>. A project is where you house your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
msgstr ""
msgid "This repository" msgid "This repository"
msgstr "" msgstr ""
...@@ -25554,6 +25563,9 @@ msgstr "" ...@@ -25554,6 +25563,9 @@ msgstr ""
msgid "Your device was successfully set up! Give it a name and register it with the GitLab server." msgid "Your device was successfully set up! Give it a name and register it with the GitLab server."
msgstr "" msgstr ""
msgid "Your first project"
msgstr ""
msgid "Your groups" msgid "Your groups"
msgstr "" msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment