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 @@
**tracking_attrs(track_label, 'click_button', 'bitbucket_cloud') do
= icon('bitbucket', text: 'Bitbucket Cloud')
- unless bitbucket_import_configured?
= render 'bitbucket_import_modal'
= render 'projects/bitbucket_import_modal'
- if bitbucket_server_import_enabled?
%div
= link_to status_import_bitbucket_server_path, class: "btn import_bitbucket", **tracking_attrs(track_label, 'click_button', 'bitbucket_server') do
......@@ -34,7 +34,7 @@
**tracking_attrs(track_label, 'click_button', 'gitlab_com') do
= icon('gitlab', text: 'GitLab.com')
- unless gitlab_import_configured?
= render 'gitlab_import_modal'
= render 'projects/gitlab_import_modal'
- if google_code_import_enabled?
%div
......@@ -73,4 +73,4 @@
= form_for @project, html: { class: 'new_project' } do |f|
%hr
= 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
Gitlab.ee do
resources :groups, only: [:new, :create]
resources :projects, only: [:new, :create]
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;
}
.edit-group,
.edit-profile {
.edit-profile,
.new-project {
max-width: 400px;
.bar {
......@@ -170,5 +171,19 @@ $subscriptions-full-width-lg: 541px;
.field_with_errors {
display: inline;
}
.project-import {
h5 {
display: none;
}
.import-buttons {
justify-content: center;
.btn {
width: 288px;
}
}
}
}
}
......@@ -5,6 +5,7 @@ module Registrations
layout 'checkout'
before_action :authorize_create_group!, only: :new
before_action :check_experiment_enabled
def new
@group = Group.new
......@@ -14,7 +15,7 @@ module Registrations
@group = Groups::CreateService.new(current_user, group_params).execute
if @group.persisted?
redirect_to @group
redirect_to new_users_sign_up_project_path(namespace_id: @group.id)
else
render action: :new
end
......@@ -26,6 +27,10 @@ module Registrations
access_denied! unless can?(current_user, :create_group)
end
def check_experiment_enabled
access_denied! unless experiment_enabled?(:onboarding_issues)
end
def group_params
params.require(:group).permit(:name, :path, :visibility_level)
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
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(:ok) }
it { is_expected.to render_template(:new) }
context 'user without the ability to create a group' do
......@@ -27,6 +27,14 @@ describe Registrations::GroupsController do
it { is_expected.to have_gitlab_http_status(:not_found) }
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
......@@ -43,23 +51,17 @@ describe Registrations::GroupsController do
context 'with an authenticated user' do
before do
sign_in(user)
stub_experiment_for_user(onboarding_issues: true)
end
it 'creates a group' do
expect { subject }.to change { Group.count }.by(1)
end
it 'redirects to the group path' do
expect(subject).to have_gitlab_http_status(:redirect)
expect(subject).to redirect_to('/group-path')
end
end
it { is_expected.to have_gitlab_http_status(:redirect) }
it { is_expected.to redirect_to(new_users_sign_up_project_path(namespace_id: user.groups.last.id)) }
context 'when the group cannot be saved' do
before do
sign_in(user)
end
let(:params) { { name: '', path: '' } }
it 'does not create a group' do
......@@ -67,9 +69,16 @@ describe Registrations::GroupsController do
expect(assigns(:group).errors).not_to be_blank
end
it 'renders the new template' do
expect(subject).to have_gitlab_http_status(:ok)
expect(subject).to render_template(:new)
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
......
# 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
before do
gitlab_sign_in(user)
stub_experiment_for_user(onboarding_issues: true)
visit new_users_sign_up_group_path
end
......@@ -14,7 +15,6 @@ describe 'New group screen', :js do
it 'shows the progress bar with the correct steps' do
expect(subject).to have_content('Create your group')
expect(subject).to have_content('1. Your profile 2. Your GitLab group 3. Your first project')
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 ""
msgid "Create your group"
msgstr ""
msgid "Create/import your first project"
msgstr ""
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
......@@ -17182,6 +17185,9 @@ msgstr ""
msgid "ProjectsNew|Contact an administrator to enable options for importing your project."
msgstr ""
msgid "ProjectsNew|Create"
msgstr ""
msgid "ProjectsNew|Create from template"
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."
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"
msgstr ""
......@@ -25554,6 +25563,9 @@ msgstr ""
msgid "Your device was successfully set up! Give it a name and register it with the GitLab server."
msgstr ""
msgid "Your first project"
msgstr ""
msgid "Your groups"
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