Commit 0768943e authored by Imre Farkas's avatar Imre Farkas

Merge branch 'growth-87-group-edit-page' into 'master'

Allow user to edit group

Closes gitlab-org/growth/engineering#96

See merge request gitlab-org/gitlab!23415
parents fe62fb88 40f72ef2
/* eslint-disable no-new */
import mountProgressBar from 'ee/subscriptions/groups/edit';
import GroupPathValidator from '~/pages/groups/new/group_path_validator';
import BindInOut from '~/behaviors/bind_in_out';
import Group from '~/group';
document.addEventListener('DOMContentLoaded', () => {
mountProgressBar();
new GroupPathValidator();
BindInOut.initAll();
new Group();
});
import Vue from 'vue';
import { PROGRESS_STEPS } from 'ee/subscriptions/new/constants';
import ProgressBar from 'ee/subscriptions/new/components/checkout/progress_bar.vue';
export default () => {
const progressBarEl = document.getElementById('progress-bar');
return new Vue({
el: progressBarEl,
render(createElement) {
return createElement(ProgressBar, { props: { step: PROGRESS_STEPS.editGroup } });
},
});
};
<script> <script>
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { PROGRESS_STEPS } from '../constants';
import ProgressBar from './checkout/progress_bar.vue'; import ProgressBar from './checkout/progress_bar.vue';
import SubscriptionDetails from './checkout/subscription_details.vue'; import SubscriptionDetails from './checkout/subscription_details.vue';
import BillingAddress from './checkout/billing_address.vue'; import BillingAddress from './checkout/billing_address.vue';
...@@ -8,6 +9,11 @@ import ConfirmOrder from './checkout/confirm_order.vue'; ...@@ -8,6 +9,11 @@ import ConfirmOrder from './checkout/confirm_order.vue';
export default { export default {
components: { ProgressBar, SubscriptionDetails, BillingAddress, PaymentMethod, ConfirmOrder }, components: { ProgressBar, SubscriptionDetails, BillingAddress, PaymentMethod, ConfirmOrder },
data() {
return {
step: PROGRESS_STEPS.checkout,
};
},
i18n: { i18n: {
checkout: s__('Checkout|Checkout'), checkout: s__('Checkout|Checkout'),
}, },
...@@ -16,7 +22,7 @@ export default { ...@@ -16,7 +22,7 @@ export default {
<template> <template>
<div class="checkout d-flex flex-column justify-content-between w-100"> <div class="checkout d-flex flex-column justify-content-between w-100">
<div class="full-width"> <div class="full-width">
<progress-bar :step="2" /> <progress-bar :step="step" />
<div class="flash-container"></div> <div class="flash-container"></div>
<h2 class="mt-4 mb-3 mb-lg-5">{{ $options.i18n.checkout }}</h2> <h2 class="mt-4 mb-3 mb-lg-5">{{ $options.i18n.checkout }}</h2>
<subscription-details /> <subscription-details />
......
...@@ -11,4 +11,10 @@ export const ZUORA_IFRAME_OVERRIDE_PARAMS = { ...@@ -11,4 +11,10 @@ export const ZUORA_IFRAME_OVERRIDE_PARAMS = {
retainValues: 'true', retainValues: 'true',
}; };
export const PROGRESS_STEPS = {
editProfile: 1,
checkout: 2,
editGroup: 3,
};
export const TAX_RATE = 0; export const TAX_RATE = 0;
...@@ -154,4 +154,16 @@ $subscriptions-full-width-lg: 541px; ...@@ -154,4 +154,16 @@ $subscriptions-full-width-lg: 541px;
max-width: none; max-width: none;
} }
} }
.edit-group {
max-width: 400px;
.bar {
width: 100%;
}
.form-check:nth-child(2) {
display: none;
}
}
} }
# frozen_string_literal: true
module Subscriptions
class GroupsController < ApplicationController
include RoutableActions
layout 'checkout'
before_action :find_group
def edit
end
def update
if Groups::UpdateService.new(@group, current_user, group_params).execute
notice = _('Welcome to GitLab, %{first_name}!' % { first_name: current_user.first_name })
redirect_to group_path(@group), notice: notice
else
@group.path = @group.path_before_last_save || @group.path_was
render action: :edit
end
end
private
def find_group
@group ||= find_routable!(Group, params[:id])
end
def group_params
params.require(:group).permit(:name, :path, :visibility_level)
end
def build_canonical_path(group)
url_for(safe_params.merge(id: group.to_param))
end
end
end
...@@ -46,7 +46,8 @@ class SubscriptionsController < ApplicationController ...@@ -46,7 +46,8 @@ class SubscriptionsController < ApplicationController
def create def create
current_user.update(setup_for_company: true) if params[:setup_for_company] current_user.update(setup_for_company: true) if params[:setup_for_company]
group_name = params[:setup_for_company] ? customer_params[:company] : "#{current_user.name}'s Group" group_name = params[:setup_for_company] ? customer_params[:company] : "#{current_user.name}'s Group"
group = Groups::CreateService.new(current_user, name: group_name, path: SecureRandom.uuid).execute path = Namespace.clean_path(group_name)
group = Groups::CreateService.new(current_user, name: group_name, path: path).execute
return render json: group.errors.to_json unless group.persisted? return render json: group.errors.to_json unless group.persisted?
response = Subscriptions::CreateService.new( response = Subscriptions::CreateService.new(
...@@ -56,7 +57,7 @@ class SubscriptionsController < ApplicationController ...@@ -56,7 +57,7 @@ class SubscriptionsController < ApplicationController
subscription_params: subscription_params subscription_params: subscription_params
).execute ).execute
response[:data] = { location: edit_group_path(group) } if response[:success] response[:data] = { location: edit_subscriptions_group_path(group.path) } if response[:success]
render json: response[:data] render json: response[:data]
end end
......
...@@ -10,6 +10,14 @@ module SubscriptionsHelper ...@@ -10,6 +10,14 @@ module SubscriptionsHelper
} }
end end
def plan_title
@plan_title ||= subscription.hosted_plan.title
end
def subscription_seats
@subscription_seats ||= subscription.seats
end
private private
def plan_data def plan_data
...@@ -18,4 +26,8 @@ module SubscriptionsHelper ...@@ -18,4 +26,8 @@ module SubscriptionsHelper
.reject { |plan| plan[:free] } .reject { |plan| plan[:free] }
.map { |plan| plan.slice(:id, :code, :price_per_year) } .map { |plan| plan.slice(:id, :code, :price_per_year) }
end end
def subscription
@subscription ||= @group.gitlab_subscription
end
end end
- page_title _('Your GitLab group')
.row.flex-grow-1.bg-gray-light
.d-flex.flex-column.align-items-center.w-100.gl-p-3
%section.gl-banner.gl-banner-introduction.gl-p-2.px-lg-6
.gl-banner-illustration.d-flex
= image_tag('illustrations/subscription-success.svg', class: 'mw-xs')
.gl-banner-content.d-flex.flex-column.justify-content-center
%h3= _('Thanks for your purchase!')
- number_of_users = n_('1 user', '%{num} users', subscription_seats) % { num: subscription_seats }
%p= _('You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email.') % { plan: plan_title, seats: number_of_users }
.edit-group.d-flex.flex-column.align-items-center.gl-pt-5
#progress-bar
%h2.center= _('Create a group for your organization')
%p
%div= _('A group represents your organization in GitLab.')
%div= _('Your %{plan} plan will be applied to your group.' % { plan: plan_title })
= form_for [:subscriptions, @group], html: { class: 'gl-show-field-errors card w-100 gl-p-3' } do |f|
= form_errors(@group)
.row
.form-group.group-name-holder.col-sm-12
= f.label :name, class: 'label-bold' do
= _('Group name (Your organization)')
= f.text_field :name, class: 'form-control',
required: true,
title: _('Please fill in a descriptive name for your group.'),
autofocus: true
.form-text.text-muted= _('You can always edit this later')
.row
.form-group.col-sm-12
= f.label :path, class: 'label-bold' do
= _('Group URL')
.input-group.gl-field-error-anchor
.group-root-path.input-group-prepend.has-tooltip{ title: group_path, :'data-placement' => 'bottom' }
.input-group-text
%span= root_url
= f.text_field :path, class: 'form-control js-validate-group-path',
autofocus: local_assigns[:autofocus] || false, required: true,
pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS,
title: _('Please choose a group URL with no special characters.')
%p.validation-error.gl-field-error.field-validation.hide
= _('Group path is already taken. Suggestions: ')
%span.gl-path-suggestions
%p.validation-success.gl-field-success.field-validation.hide= _('Group path is available.')
%p.validation-pending.gl-field-error-ignore.field-validation.hide= _('Checking group path availability...')
.row
.form-group.col-sm-12
= f.label :visibility_level, class: 'label-bold' do
= _('Visibility level')
= render 'shared/visibility_level', f: f, visibility_level: default_group_visibility, can_change_visibility_level: true, form_model: @group, with_label: false
.row
.form-group.col-sm-12.mb-0
= button_tag class: %w[btn btn-success w-100] do
= _('Get started')
...@@ -3,4 +3,8 @@ ...@@ -3,4 +3,8 @@
resource :subscriptions, only: [:new, :create] do resource :subscriptions, only: [:new, :create] do
get :payment_form get :payment_form
get :payment_method get :payment_method
scope module: :subscriptions do
resources :groups, only: [:edit, :update]
end
end end
# frozen_string_literal: true
require 'spec_helper'
describe Subscriptions::GroupsController do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
describe 'GET #edit' do
subject { get :edit, params: { id: group.to_param } }
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)
end
it { is_expected.to have_gitlab_http_status(:ok) }
end
end
describe 'PUT #update' do
subject { post :update, params: { id: group.to_param, group: params } }
let(:params) { { name: 'New name', path: 'new-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 who is not a group owner' do
before do
sign_in(user)
end
it { is_expected.to have_gitlab_http_status(:ok) }
it { is_expected.to render_template(:edit) }
it 'does not update the name' do
expect { subject }.not_to change { group.reload.name }
end
end
context 'with an authenticated user' do
before do
sign_in(user)
group.add_owner(user)
end
it 'updates the name' do
expect { subject }.to change { group.reload.name }.to('New name')
end
it 'updates the path' do
expect { subject }.to change { group.reload.path }.to('new-path')
end
it 'updates the visibility_level' do
expect { subject }.to change { group.reload.visibility_level }.from(Gitlab::VisibilityLevel::PUBLIC).to(Gitlab::VisibilityLevel::PRIVATE)
end
it 'redirects to the group path' do
expect(subject).to have_gitlab_http_status(:redirect)
expect(subject).to redirect_to('/new-path')
end
end
context 'when the group cannot be saved' do
before do
sign_in(user)
group.add_owner(user)
end
let(:params) { { name: '', path: '' } }
it 'does not update the name' do
expect { subject }.not_to change { group.reload.name }
end
it 'does not update the path' do
expect { subject }.not_to change { group.reload.path }
end
it 're-renders the edit template' do
expect(subject).to have_gitlab_http_status(:ok)
expect(subject).to render_template(:edit)
end
end
end
end
...@@ -98,7 +98,7 @@ describe SubscriptionsController do ...@@ -98,7 +98,7 @@ describe SubscriptionsController do
post :create, post :create,
params: { params: {
setup_for_company: setup_for_company, setup_for_company: setup_for_company,
customer: { country: 'NL' }, customer: { company: 'My company', country: 'NL' },
subscription: { plan_id: 'x' } subscription: { plan_id: 'x' }
}, },
as: :json as: :json
...@@ -157,7 +157,7 @@ describe SubscriptionsController do ...@@ -157,7 +157,7 @@ describe SubscriptionsController do
it 'returns the group edit location in JSON format' do it 'returns the group edit location in JSON format' do
subject subject
expect(response.body).to eq({ location: "/groups/#{group.path}/-/edit" }.to_json) expect(response.body).to eq({ location: "/-/subscriptions/groups/#{group.path}/edit" }.to_json)
end end
end end
......
...@@ -34,4 +34,28 @@ describe SubscriptionsHelper do ...@@ -34,4 +34,28 @@ describe SubscriptionsHelper do
it { is_expected.to include(plan_data: '[{"id":"bronze_id","code":"bronze","price_per_year":48.0}]') } it { is_expected.to include(plan_data: '[{"id":"bronze_id","code":"bronze","price_per_year":48.0}]') }
it { is_expected.to include(plan_id: 'bronze_id') } it { is_expected.to include(plan_id: 'bronze_id') }
end end
describe '#plan_title' do
let_it_be(:subscription) { create(:gitlab_subscription) }
before do
allow(helper).to receive(:subscription).and_return(subscription)
end
subject { helper.plan_title }
it { is_expected.to eq(subscription.hosted_plan.title) }
end
describe '#subscription_seats' do
let_it_be(:subscription) { create(:gitlab_subscription) }
before do
allow(helper).to receive(:subscription).and_return(subscription)
end
subject { helper.subscription_seats }
it { is_expected.to eq(subscription.seats) }
end
end end
...@@ -627,7 +627,7 @@ msgstr[0] "" ...@@ -627,7 +627,7 @@ msgstr[0] ""
msgstr[1] "" msgstr[1] ""
msgid "1 user" msgid "1 user"
msgid_plural "%d users" msgid_plural "%{num} users"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
...@@ -772,6 +772,9 @@ msgstr "" ...@@ -772,6 +772,9 @@ msgstr ""
msgid "A fork is a copy of a project.<br />Forking a repository allows you to make changes without affecting the original project." msgid "A fork is a copy of a project.<br />Forking a repository allows you to make changes without affecting the original project."
msgstr "" msgstr ""
msgid "A group represents your organization in GitLab."
msgstr ""
msgid "A member of the abuse team will review your report as soon as possible." msgid "A member of the abuse team will review your report as soon as possible."
msgstr "" msgstr ""
...@@ -5406,6 +5409,9 @@ msgstr "" ...@@ -5406,6 +5409,9 @@ msgstr ""
msgid "Create a Mattermost team for this group" msgid "Create a Mattermost team for this group"
msgstr "" msgstr ""
msgid "Create a group for your organization"
msgstr ""
msgid "Create a local proxy for storing frequently used upstream images. %{link_start}Learn more%{link_end} about dependency proxies." msgid "Create a local proxy for storing frequently used upstream images. %{link_start}Learn more%{link_end} about dependency proxies."
msgstr "" msgstr ""
...@@ -8930,6 +8936,9 @@ msgstr "" ...@@ -8930,6 +8936,9 @@ msgstr ""
msgid "Get a free instance review" msgid "Get a free instance review"
msgstr "" msgstr ""
msgid "Get started"
msgstr ""
msgid "Get started with error tracking" msgid "Get started with error tracking"
msgstr "" msgstr ""
...@@ -9395,6 +9404,9 @@ msgstr "" ...@@ -9395,6 +9404,9 @@ msgstr ""
msgid "Group name" msgid "Group name"
msgstr "" msgstr ""
msgid "Group name (Your organization)"
msgstr ""
msgid "Group overview" msgid "Group overview"
msgstr "" msgstr ""
...@@ -18463,6 +18475,9 @@ msgstr "" ...@@ -18463,6 +18475,9 @@ msgstr ""
msgid "Thank you for your report. A GitLab administrator will look into it shortly." msgid "Thank you for your report. A GitLab administrator will look into it shortly."
msgstr "" msgstr ""
msgid "Thanks for your purchase!"
msgstr ""
msgid "Thanks! Don't show me this again" msgid "Thanks! Don't show me this again"
msgstr "" msgstr ""
...@@ -21068,6 +21083,9 @@ msgstr "" ...@@ -21068,6 +21083,9 @@ msgstr ""
msgid "Welcome to GitLab %{name}!" msgid "Welcome to GitLab %{name}!"
msgstr "" msgstr ""
msgid "Welcome to GitLab, %{first_name}!"
msgstr ""
msgid "Welcome to the Guided GitLab Tour" msgid "Welcome to the Guided GitLab Tour"
msgstr "" msgstr ""
...@@ -21382,6 +21400,9 @@ msgstr "" ...@@ -21382,6 +21400,9 @@ msgstr ""
msgid "You can also upload existing files from your computer using the instructions below." msgid "You can also upload existing files from your computer using the instructions below."
msgstr "" msgstr ""
msgid "You can always edit this later"
msgstr ""
msgid "You can apply your Trial to your Personal account or create a New Group." msgid "You can apply your Trial to your Personal account or create a New Group."
msgstr "" msgstr ""
...@@ -21556,6 +21577,9 @@ msgstr "" ...@@ -21556,6 +21577,9 @@ msgstr ""
msgid "You have reached your project limit" msgid "You have reached your project limit"
msgstr "" msgstr ""
msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email."
msgstr ""
msgid "You haven't added any issues to your project yet" msgid "You haven't added any issues to your project yet"
msgstr "" msgstr ""
...@@ -21715,6 +21739,9 @@ msgstr "" ...@@ -21715,6 +21739,9 @@ msgstr ""
msgid "Your GPG keys (%{count})" msgid "Your GPG keys (%{count})"
msgstr "" msgstr ""
msgid "Your GitLab group"
msgstr ""
msgid "Your Gitlab Gold trial will last 30 days after which point you can keep your free Gitlab account forever. We just need some additional information to activate your trial." msgid "Your Gitlab Gold trial will last 30 days after which point you can keep your free Gitlab account forever. We just need some additional information to activate your trial."
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