Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
05d5910e
Commit
05d5910e
authored
May 17, 2021
by
Vitaly Slobodin
Committed by
Natalia Tepluhina
May 17, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Migrate OrderSummary component to GraphQL
parent
8f84338e
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
491 additions
and
5 deletions
+491
-5
ee/app/assets/javascripts/subscriptions/buy_minutes/components/app.vue
.../javascripts/subscriptions/buy_minutes/components/app.vue
+5
-1
ee/app/assets/javascripts/subscriptions/buy_minutes/components/checkout.vue
...scripts/subscriptions/buy_minutes/components/checkout.vue
+2
-2
ee/app/assets/javascripts/subscriptions/buy_minutes/components/order_summary.vue
...ts/subscriptions/buy_minutes/components/order_summary.vue
+153
-0
ee/app/assets/javascripts/subscriptions/buy_minutes/components/order_summary/summary_details.vue
.../buy_minutes/components/order_summary/summary_details.vue
+104
-0
ee/app/assets/javascripts/subscriptions/buy_minutes/graphql/resolvers.js
...avascripts/subscriptions/buy_minutes/graphql/resolvers.js
+2
-2
ee/spec/frontend/subscriptions/buy_minutes/components/checkout/order_summary_spec.js
...ons/buy_minutes/components/checkout/order_summary_spec.js
+225
-0
No files found.
ee/app/assets/javascripts/subscriptions/buy_minutes/components/app.vue
View file @
05d5910e
...
...
@@ -7,11 +7,13 @@ import { ERROR_FETCHING_DATA_HEADER, ERROR_FETCHING_DATA_DESCRIPTION } from '~/e
import
plansQuery
from
'
../../graphql/queries/plans.customer.query.graphql
'
;
import
{
planTags
,
CUSTOMER_CLIENT
}
from
'
../constants
'
;
import
Checkout
from
'
./checkout.vue
'
;
import
OrderSummary
from
'
./order_summary.vue
'
;
export
default
{
components
:
{
Checkout
,
GlEmptyState
,
OrderSummary
,
StepOrderApp
,
},
i18n
:
{
...
...
@@ -59,6 +61,8 @@ export default {
<template
#checkout
>
<checkout
:plans=
"plans"
/>
</
template
>
<
template
#order-summary
></
template
>
<
template
#order-summary
>
<order-summary
:plans=
"plans"
/>
</
template
>
</step-order-app>
</template>
ee/app/assets/javascripts/subscriptions/buy_minutes/components/checkout.vue
View file @
05d5910e
...
...
@@ -33,12 +33,12 @@ export default {
<
template
>
<div
v-if=
"!$apollo.loading"
class=
"checkout gl-
flex gl-flex
-column gl-justify-content-between w-100"
class=
"checkout gl-
display-flex gl-flex-direction
-column gl-justify-content-between w-100"
>
<div
class=
"full-width"
>
<progress-bar
v-if=
"isNewUser"
:steps=
"$options.steps"
:current-step=
"$options.currentStep"
/>
<div
class=
"flash-container"
></div>
<h2
class=
"gl-mt-
4 gl-mb-3
gl-mb-lg-5"
>
{{
$options
.
i18n
.
checkout
}}
</h2>
<h2
class=
"gl-mt-
6 gl-mb-7
gl-mb-lg-5"
>
{{
$options
.
i18n
.
checkout
}}
</h2>
<subscription-details
:plans=
"plans"
/>
</div>
</div>
...
...
ee/app/assets/javascripts/subscriptions/buy_minutes/components/order_summary.vue
0 → 100644
View file @
05d5910e
<
script
>
import
{
GlIcon
}
from
'
@gitlab/ui
'
;
import
STATE_QUERY
from
'
ee/subscriptions/graphql/queries/state.query.graphql
'
;
import
{
TAX_RATE
,
NEW_GROUP
}
from
'
ee/subscriptions/new/constants
'
;
import
formattingMixins
from
'
ee/subscriptions/new/formatting_mixins
'
;
import
{
sprintf
,
s__
}
from
'
~/locale
'
;
import
SummaryDetails
from
'
./order_summary/summary_details.vue
'
;
export
default
{
components
:
{
SummaryDetails
,
GlIcon
,
},
mixins
:
[
formattingMixins
],
props
:
{
plans
:
{
type
:
Array
,
required
:
true
,
},
},
apollo
:
{
state
:
{
query
:
STATE_QUERY
,
update
(
data
)
{
return
data
;
},
result
({
data
})
{
this
.
subscription
=
data
.
subscription
;
this
.
namespaces
=
data
.
namespaces
;
this
.
isSetupForCompany
=
data
.
isSetupForCompany
;
this
.
fullName
=
data
.
fullName
;
this
.
customer
=
data
.
customer
;
},
},
},
data
()
{
return
{
subscription
:
{},
namespaces
:
[],
isSetupForCompany
:
false
,
collapsed
:
true
,
fullName
:
null
,
customer
:
{},
};
},
computed
:
{
selectedPlan
()
{
return
this
.
plans
.
find
((
plan
)
=>
plan
.
code
===
this
.
subscription
.
planId
);
},
selectedPlanPrice
()
{
return
this
.
selectedPlan
.
pricePerYear
;
},
selectedGroup
()
{
return
this
.
namespaces
.
find
((
group
)
=>
group
.
id
===
this
.
subscription
.
namespaceId
);
},
totalExVat
()
{
return
this
.
subscription
.
quantity
*
this
.
selectedPlanPrice
;
},
vat
()
{
return
TAX_RATE
*
this
.
totalExVat
;
},
totalAmount
()
{
return
this
.
totalExVat
+
this
.
vat
;
},
usersPresent
()
{
return
this
.
subscription
.
quantity
>
0
;
},
isGroupSelected
()
{
return
this
.
subscription
.
namespaceId
&&
this
.
subscription
.
namespaceId
!==
NEW_GROUP
;
},
isSelectedGroupPresent
()
{
return
(
this
.
isGroupSelected
&&
this
.
namespaces
.
some
((
namespace
)
=>
namespace
.
id
===
this
.
subscription
.
namespaceId
)
);
},
name
()
{
if
(
this
.
isSetupForCompany
&&
this
.
customer
.
company
)
{
return
this
.
customer
.
company
;
}
if
(
this
.
isGroupSelected
&&
this
.
isSelectedGroupPresent
)
{
return
this
.
selectedGroup
.
name
;
}
if
(
this
.
isSetupForCompany
)
{
return
s__
(
'
Checkout|Your organization
'
);
}
return
this
.
fullName
;
},
titleWithName
()
{
return
sprintf
(
this
.
$options
.
i18n
.
title
,
{
name
:
this
.
name
});
},
},
methods
:
{
toggleCollapse
()
{
this
.
collapsed
=
!
this
.
collapsed
;
},
},
i18n
:
{
title
:
s__
(
"
Checkout|%{name}'s GitLab subscription
"
),
},
taxRate
:
TAX_RATE
,
};
</
script
>
<
template
>
<div
v-if=
"!$apollo.loading && (!isGroupSelected || isSelectedGroupPresent)"
class=
"order-summary gl-display-flex gl-flex-direction-column gl-flex-grow-1 gl-mt-2 mt-lg-5"
>
<div
class=
"d-lg-none"
>
<div
@
click=
"toggleCollapse"
>
<h4
class=
"d-flex justify-content-between gl-font-lg"
:class=
"
{ 'gl-mb-7': !collapsed }">
<div
class=
"d-flex"
>
<gl-icon
v-if=
"collapsed"
name=
"chevron-right"
:size=
"18"
use-deprecated-sizes
/>
<gl-icon
v-else
name=
"chevron-down"
:size=
"18"
use-deprecated-sizes
/>
<div>
{{
titleWithName
}}
</div>
</div>
<div
class=
"gl-ml-3"
>
{{
formatAmount
(
totalAmount
,
usersPresent
)
}}
</div>
</h4>
</div>
<summary-details
v-show=
"!collapsed"
:vat=
"vat"
:total-ex-vat=
"totalExVat"
:users-present=
"usersPresent"
:selected-plan-text=
"selectedPlan.name"
:selected-plan-price=
"selectedPlanPrice"
:total-amount=
"totalAmount"
:number-of-users=
"subscription.quantity"
:tax-rate=
"$options.taxRate"
/>
</div>
<div
class=
"d-none d-lg-block"
>
<div
class=
"append-bottom-20"
>
<h4>
{{
titleWithName
}}
</h4>
</div>
<summary-details
:vat=
"vat"
:total-ex-vat=
"totalExVat"
:users-present=
"usersPresent"
:selected-plan-text=
"selectedPlan.name"
:selected-plan-price=
"selectedPlanPrice"
:total-amount=
"totalAmount"
:number-of-users=
"subscription.quantity"
:tax-rate=
"$options.taxRate"
/>
</div>
</div>
</
template
>
ee/app/assets/javascripts/subscriptions/buy_minutes/components/order_summary/summary_details.vue
0 → 100644
View file @
05d5910e
<
script
>
import
formattingMixins
from
'
ee/subscriptions/new/formatting_mixins
'
;
import
{
s__
}
from
'
~/locale
'
;
export
default
{
mixins
:
[
formattingMixins
],
props
:
{
vat
:
{
type
:
Number
,
required
:
true
,
},
totalExVat
:
{
type
:
Number
,
required
:
true
,
},
usersPresent
:
{
type
:
Boolean
,
required
:
true
,
},
selectedPlanText
:
{
type
:
String
,
required
:
true
,
},
selectedPlanPrice
:
{
type
:
Number
,
required
:
true
,
},
totalAmount
:
{
type
:
Number
,
required
:
true
,
},
numberOfUsers
:
{
type
:
Number
,
required
:
true
,
},
taxRate
:
{
type
:
Number
,
required
:
false
,
default
:
null
,
},
},
computed
:
{
startDate
()
{
return
new
Date
(
Date
.
now
());
},
endDate
()
{
return
new
Date
(
this
.
startDate
).
setFullYear
(
this
.
startDate
.
getFullYear
()
+
1
);
},
},
i18n
:
{
selectedPlanText
:
s__
(
'
Checkout|%{selectedPlanText} plan
'
),
numberOfUsers
:
s__
(
'
Checkout|(x%{numberOfUsers})
'
),
pricePerUserPerYear
:
s__
(
'
Checkout|$%{selectedPlanPrice} per user per year
'
),
dates
:
s__
(
'
Checkout|%{startDate} - %{endDate}
'
),
subtotal
:
s__
(
'
Checkout|Subtotal
'
),
tax
:
s__
(
'
Checkout|Tax
'
),
total
:
s__
(
'
Checkout|Total
'
),
},
};
</
script
>
<
template
>
<div>
<div
class=
"d-flex justify-content-between bold gl-mt-3 gl-mb-3"
>
<div
class=
"js-selected-plan"
>
{{
sprintf
(
$options
.
i18n
.
selectedPlanText
,
{
selectedPlanText
}
)
}}
<
span
v
-
if
=
"
usersPresent
"
class
=
"
js-number-of-users
"
>
{{
sprintf
(
$options
.
i18n
.
numberOfUsers
,
{
numberOfUsers
}
)
}}
<
/span
>
<
/div
>
<
div
class
=
"
js-amount
"
>
{{
formatAmount
(
totalExVat
,
usersPresent
)
}}
<
/div
>
<
/div
>
<
div
class
=
"
text-secondary js-per-user
"
>
{{
sprintf
(
$options
.
i18n
.
pricePerUserPerYear
,
{
selectedPlanPrice
:
selectedPlanPrice
.
toLocaleString
(),
}
)
}}
<
/div
>
<
div
class
=
"
text-secondary js-dates
"
>
{{
sprintf
(
$options
.
i18n
.
dates
,
{
startDate
:
formatDate
(
startDate
),
endDate
:
formatDate
(
endDate
),
}
)
}}
<
/div
>
<
div
v
-
if
=
"
taxRate
"
>
<
div
class
=
"
border-bottom gl-mt-3 gl-mb-3
"
><
/div
>
<
div
class
=
"
d-flex justify-content-between text-secondary
"
>
<
div
>
{{
$options
.
i18n
.
subtotal
}}
<
/div
>
<
div
class
=
"
js-total-ex-vat
"
>
{{
formatAmount
(
totalExVat
,
usersPresent
)
}}
<
/div
>
<
/div
>
<
div
class
=
"
d-flex justify-content-between text-secondary
"
>
<
div
>
{{
$options
.
i18n
.
tax
}}
<
/div
>
<
div
class
=
"
js-vat
"
>
{{
formatAmount
(
vat
,
usersPresent
)
}}
<
/div
>
<
/div
>
<
/div
>
<
div
class
=
"
border-bottom gl-mt-3 gl-mb-3
"
><
/div
>
<
div
class
=
"
d-flex justify-content-between bold gl-font-lg
"
>
<
div
>
{{
$options
.
i18n
.
total
}}
<
/div
>
<
div
class
=
"
js-total-amount
"
>
{{
formatAmount
(
totalAmount
,
usersPresent
)
}}
<
/div
>
<
/div
>
<
/div
>
<
/template
>
ee/app/assets/javascripts/subscriptions/buy_minutes/graphql/resolvers.js
View file @
05d5910e
...
...
@@ -34,13 +34,13 @@ export const resolvers = {
return
SubscriptionsApi
.
createSubscription
(
groupId
,
customer
,
subscription
);
},
updateState
:
(
_
,
{
input
},
{
cache
})
=>
{
const
{
state
:
oldState
}
=
cache
.
readQuery
({
query
:
STATE_QUERY
});
const
oldState
=
cache
.
readQuery
({
query
:
STATE_QUERY
});
const
state
=
produce
(
oldState
,
(
draftState
)
=>
{
merge
(
draftState
,
input
);
});
cache
.
writeQuery
({
query
:
STATE_QUERY
,
data
:
{
state
}
});
cache
.
writeQuery
({
query
:
STATE_QUERY
,
data
:
state
});
},
},
};
ee/spec/frontend/subscriptions/buy_minutes/components/checkout/order_summary_spec.js
0 → 100644
View file @
05d5910e
import
{
mount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
{
merge
}
from
'
lodash
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
OrderSummary
from
'
ee/subscriptions/buy_minutes/components/order_summary.vue
'
;
import
subscriptionsResolvers
from
'
ee/subscriptions/buy_minutes/graphql/resolvers
'
;
import
stateQuery
from
'
ee/subscriptions/graphql/queries/state.query.graphql
'
;
import
purchaseFlowResolvers
from
'
ee/vue_shared/purchase_flow/graphql/resolvers
'
;
import
{
mockCiMinutesPlans
,
stateData
as
mockStateData
,
}
from
'
ee_jest/subscriptions/buy_minutes/mock_data
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
const
localVue
=
createLocalVue
();
localVue
.
use
(
VueApollo
);
describe
(
'
Order Summary
'
,
()
=>
{
const
resolvers
=
{
...
purchaseFlowResolvers
,
...
subscriptionsResolvers
};
const
initialStateData
=
{
subscription
:
{
planId
:
'
silver
'
},
};
let
wrapper
;
const
createMockApolloProvider
=
(
stateData
=
{})
=>
{
const
mockApollo
=
createMockApollo
([],
resolvers
);
const
data
=
merge
({},
mockStateData
,
initialStateData
,
stateData
);
mockApollo
.
clients
.
defaultClient
.
cache
.
writeQuery
({
query
:
stateQuery
,
data
,
});
return
mockApollo
;
};
const
createComponent
=
(
stateData
)
=>
{
const
apolloProvider
=
createMockApolloProvider
(
stateData
);
wrapper
=
mount
(
OrderSummary
,
{
localVue
,
apolloProvider
,
propsData
:
{
plans
:
mockCiMinutesPlans
,
},
});
};
afterEach
(()
=>
{
wrapper
.
destroy
();
});
describe
(
'
Changing the company name
'
,
()
=>
{
describe
(
'
When purchasing for a single user
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
isSetupForCompany
:
false
});
});
it
(
'
should display the title with the passed name
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
h4
'
).
text
()).
toContain
(
"
Full Name's GitLab subscription
"
);
});
});
describe
(
'
When purchasing for a company or group
'
,
()
=>
{
describe
(
'
Without a group name provided
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
isSetupForCompany
:
true
});
});
it
(
'
should display the title with the default name
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
h4
'
).
text
()).
toContain
(
"
Your organization's GitLab subscription
"
);
});
});
describe
(
'
With a group name provided
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
isSetupForCompany
:
true
,
customer
:
{
company
:
'
My group
'
},
});
});
it
(
'
when given a group name, it should display the title with the group name
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
h4
'
).
text
()).
toContain
(
"
My group's GitLab subscription
"
);
});
});
});
});
describe
(
'
Changing the plan
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
();
});
describe
(
'
the selected plan
'
,
()
=>
{
it
(
'
should display the chosen plan
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-selected-plan
'
).
text
()).
toContain
(
'
silver plan
'
);
});
it
(
'
should display the correct formatted amount price per user
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-per-user
'
).
text
()).
toContain
(
'
$228 per user per year
'
);
});
});
describe
(
'
the default plan
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
subscription
:
{
planId
:
'
bronze
'
,
quantity
:
1
},
});
});
it
(
'
should display the chosen plan
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-selected-plan
'
).
text
()).
toContain
(
'
bronze plan
'
);
});
it
(
'
should display the correct formatted amount price per user
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-per-user
'
).
text
()).
toContain
(
'
$48 per user per year
'
);
});
it
(
'
should display the correct formatted total amount
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-total-amount
'
).
text
()).
toContain
(
'
$48
'
);
});
});
});
describe
(
'
Changing the number of users
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
subscription
:
{
planId
:
'
silver
'
,
quantity
:
1
},
});
});
describe
(
'
the default of 1 selected user
'
,
()
=>
{
it
(
'
should display the correct number of users
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-number-of-users
'
).
text
()).
toContain
(
'
(x1)
'
);
});
it
(
'
should display the correct formatted amount price per user
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-per-user
'
).
text
()).
toContain
(
'
$228 per user per year
'
);
});
it
(
'
should display the correct multiplied formatted amount of the chosen plan
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-amount
'
).
text
()).
toContain
(
'
$228
'
);
});
it
(
'
should display the correct formatted total amount
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-total-amount
'
).
text
()).
toContain
(
'
$228
'
);
});
});
describe
(
'
3 selected users
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
subscription
:
{
planId
:
'
silver
'
,
quantity
:
3
},
});
});
it
(
'
should display the correct number of users
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-number-of-users
'
).
text
()).
toContain
(
'
(x3)
'
);
});
it
(
'
should display the correct formatted amount price per user
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-per-user
'
).
text
()).
toContain
(
'
$228 per user per year
'
);
});
it
(
'
should display the correct multiplied formatted amount of the chosen plan
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-amount
'
).
text
()).
toContain
(
'
$684
'
);
});
it
(
'
should display the correct formatted total amount
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-total-amount
'
).
text
()).
toContain
(
'
$684
'
);
});
});
describe
(
'
no selected users
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
subscription
:
{
planId
:
'
silver
'
,
quantity
:
0
},
});
});
it
(
'
should not display the number of users
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-number-of-users
'
).
exists
()).
toBe
(
false
);
});
it
(
'
should display the correct formatted amount price per user
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-per-user
'
).
text
()).
toContain
(
'
$228 per user per year
'
);
});
it
(
'
should not display the amount
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-amount
'
).
text
()).
toContain
(
'
-
'
);
});
it
(
'
should display the correct formatted total amount
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-total-amount
'
).
text
()).
toContain
(
'
-
'
);
});
});
describe
(
'
date range
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
();
});
it
(
'
shows the formatted date range from the start date to one year in the future
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-dates
'
).
text
()).
toContain
(
'
Jul 6, 2020 - Jul 6, 2021
'
);
});
});
describe
(
'
tax rate
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
();
});
describe
(
'
a tax rate of 0
'
,
()
=>
{
it
(
'
should not display the total amount excluding vat
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-total-ex-vat
'
).
exists
()).
toBe
(
false
);
});
it
(
'
should not display the vat amount
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-vat
'
).
exists
()).
toBe
(
false
);
});
});
});
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment