2021-04-17 20:07:23 +05:30
< script >
import { GlIcon , GlEmptyState , GlLoadingIcon , GlSprintf } from '@gitlab/ui' ;
import Cookies from 'js-cookie' ;
2021-06-08 01:23:25 +05:30
import { mapActions , mapState } from 'vuex' ;
2021-04-17 20:07:23 +05:30
import { _ _ } from '~/locale' ;
import banner from './banner.vue' ;
import stageCodeComponent from './stage_code_component.vue' ;
import stageComponent from './stage_component.vue' ;
import stageNavItem from './stage_nav_item.vue' ;
import stageReviewComponent from './stage_review_component.vue' ;
import stageStagingComponent from './stage_staging_component.vue' ;
import stageTestComponent from './stage_test_component.vue' ;
const OVERVIEW _DIALOG _COOKIE = 'cycle_analytics_help_dismissed' ;
export default {
name : 'CycleAnalytics' ,
components : {
GlIcon ,
GlEmptyState ,
GlLoadingIcon ,
GlSprintf ,
banner ,
'stage-issue-component' : stageComponent ,
'stage-plan-component' : stageComponent ,
'stage-code-component' : stageCodeComponent ,
'stage-test-component' : stageTestComponent ,
'stage-review-component' : stageReviewComponent ,
'stage-staging-component' : stageStagingComponent ,
'stage-production-component' : stageComponent ,
'stage-nav-item' : stageNavItem ,
} ,
props : {
noDataSvgPath : {
type : String ,
required : true ,
} ,
noAccessSvgPath : {
type : String ,
required : true ,
} ,
} ,
data ( ) {
return {
isOverviewDialogDismissed : Cookies . get ( OVERVIEW _DIALOG _COOKIE ) ,
} ;
} ,
computed : {
2021-06-08 01:23:25 +05:30
... mapState ( [
'isLoading' ,
'isLoadingStage' ,
'isEmptyStage' ,
'selectedStage' ,
'selectedStageEvents' ,
'stages' ,
'summary' ,
'startDate' ,
] ) ,
displayStageEvents ( ) {
const { selectedStageEvents , isLoadingStage , isEmptyStage } = this ;
return selectedStageEvents . length && ! isLoadingStage && ! isEmptyStage ;
} ,
displayNotEnoughData ( ) {
const { selectedStage , isEmptyStage , isLoadingStage } = this ;
return selectedStage && isEmptyStage && ! isLoadingStage ;
} ,
displayNoAccess ( ) {
const { selectedStage } = this ;
return selectedStage && ! selectedStage . isUserAllowed ;
2021-04-17 20:07:23 +05:30
} ,
} ,
methods : {
2021-06-08 01:23:25 +05:30
... mapActions ( [
'fetchCycleAnalyticsData' ,
'fetchStageData' ,
'setSelectedStage' ,
'setDateRange' ,
] ) ,
2021-04-17 20:07:23 +05:30
handleDateSelect ( startDate ) {
2021-06-08 01:23:25 +05:30
this . setDateRange ( { startDate } ) ;
this . fetchCycleAnalyticsData ( ) ;
2021-04-17 20:07:23 +05:30
} ,
2021-06-08 01:23:25 +05:30
isActiveStage ( stage ) {
return stage . slug === this . selectedStage . slug ;
2021-04-17 20:07:23 +05:30
} ,
selectStage ( stage ) {
2021-06-08 01:23:25 +05:30
if ( this . selectedStage === stage ) return ;
2021-04-17 20:07:23 +05:30
2021-06-08 01:23:25 +05:30
this . setSelectedStage ( stage ) ;
2021-04-17 20:07:23 +05:30
if ( ! stage . isUserAllowed ) {
return ;
}
2021-06-08 01:23:25 +05:30
this . fetchStageData ( ) ;
2021-04-17 20:07:23 +05:30
} ,
dismissOverviewDialog ( ) {
this . isOverviewDialogDismissed = true ;
Cookies . set ( OVERVIEW _DIALOG _COOKIE , '1' , { expires : 365 } ) ;
} ,
} ,
dayRangeOptions : [ 7 , 30 , 90 ] ,
i18n : {
dropdownText : _ _ ( 'Last %{days} days' ) ,
} ,
} ;
< / script >
< template >
< div class = "cycle-analytics" >
< gl-loading-icon v-if = "isLoading" size="lg" / >
< div v -else class = "wrapper" >
< div class = "card" >
< div class = "card-header" > { { _ _ ( 'Recent Project Activity' ) } } < / div >
< div class = "d-flex justify-content-between" >
2021-06-08 01:23:25 +05:30
< div v-for = "item in summary" :key="item.title" class="gl-flex-grow-1 gl-text-center" >
2021-04-17 20:07:23 +05:30
< h3 class = "header" > { { item . value } } < / h3 >
< p class = "text" > { { item . title } } < / p >
< / div >
< div class = "flex-grow align-self-center text-center" >
< div class = "js-ca-dropdown dropdown inline" >
2021-06-08 01:23:25 +05:30
<!-- eslint - disable - next - line @ gitlab / vue - no - data - toggle -- >
2021-04-17 20:07:23 +05:30
< button class = "dropdown-menu-toggle" data -toggle = " dropdown " type = "button" >
< span class = "dropdown-label" >
< gl-sprintf :message = "$options.i18n.dropdownText" >
< template # days > { { startDate } } < / template >
< / gl-sprintf >
< gl-icon name = "chevron-down" class = "dropdown-menu-toggle-icon gl-top-3" / >
< / span >
< / button >
< ul class = "dropdown-menu dropdown-menu-right" >
< li v-for = "days in $options.dayRangeOptions" :key="`day-range-${days}`" >
< a href = "#" @click.prevent ="handleDateSelect(days)" >
< gl-sprintf :message = "$options.i18n.dropdownText" >
< template # days > { { days } } < / template >
< / gl-sprintf >
< / a >
< / li >
< / ul >
< / div >
< / div >
< / div >
< / div >
< div class = "stage-panel-container" >
< div class = "card stage-panel" >
< div class = "card-header border-bottom-0" >
< nav class = "col-headers" >
< ul >
< li class = "stage-header pl-5" >
< span class = "stage-name font-weight-bold" > { {
s _ _ ( 'ProjectLifecycle|Stage' )
} } < / span >
< span
class = "has-tooltip"
data - placement = "top"
: title = "__('The phase of the development lifecycle.')"
aria - hidden = "true"
>
< gl-icon name = "question-o" class = "gl-text-gray-500" / >
< / span >
< / li >
< li class = "median-header" >
< span class = "stage-name font-weight-bold" > { { _ _ ( 'Median' ) } } < / span >
< span
class = "has-tooltip"
data - placement = "top"
: title = "
_ _ (
'The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.' ,
)
"
aria - hidden = "true"
>
< gl-icon name = "question-o" class = "gl-text-gray-500" / >
< / span >
< / li >
< li class = "event-header pl-3" >
2021-06-08 01:23:25 +05:30
< span v-if = "selectedStage" class="stage-name font-weight-bold" > {{
selectedStage . legend ? _ _ ( selectedStage . legend ) : _ _ ( 'Related Issues' )
} } < / span >
2021-04-17 20:07:23 +05:30
< span
class = "has-tooltip"
data - placement = "top"
: title = "
_ _ ( 'The collection of events added to the data gathered for that stage.' )
"
aria - hidden = "true"
>
< gl-icon name = "question-o" class = "gl-text-gray-500" / >
< / span >
< / li >
< li class = "total-time-header pr-5 text-right" >
< span class = "stage-name font-weight-bold" > { { _ _ ( 'Time' ) } } < / span >
< span
class = "has-tooltip"
data - placement = "top"
: title = "__('The time taken by each data entry gathered by that stage.')"
aria - hidden = "true"
>
< gl-icon name = "question-o" class = "gl-text-gray-500" / >
< / span >
< / li >
< / ul >
< / nav >
< / div >
< div class = "stage-panel-body" >
< nav class = "stage-nav" >
< ul >
< stage-nav-item
2021-06-08 01:23:25 +05:30
v - for = "stage in stages"
2021-04-17 20:07:23 +05:30
: key = "stage.title"
: title = "stage.title"
: is - user - allowed = "stage.isUserAllowed"
: value = "stage.value"
2021-06-08 01:23:25 +05:30
: is - active = "isActiveStage(stage)"
2021-04-17 20:07:23 +05:30
@ select = "selectStage(stage)"
/ >
< / ul >
< / nav >
< section class = "stage-events overflow-auto" >
< gl-loading-icon v-show = "isLoadingStage" size="lg" / >
2021-06-08 01:23:25 +05:30
< template v-if = "displayNoAccess" >
2021-04-17 20:07:23 +05:30
< gl-empty-state
class = "js-empty-state"
: title = "__('You need permission.')"
: svg - path = "noAccessSvgPath"
: description = "__('Want to see the data? Please ask an administrator for access.')"
/ >
< / template >
< template v-else >
2021-06-08 01:23:25 +05:30
< template v-if = "displayNotEnoughData" >
2021-04-17 20:07:23 +05:30
< gl-empty-state
class = "js-empty-state"
2021-06-08 01:23:25 +05:30
: description = "selectedStage.emptyStageText"
2021-04-17 20:07:23 +05:30
: svg - path = "noDataSvgPath"
: title = "__('We don\'t have enough data to show this stage.')"
/ >
< / template >
2021-06-08 01:23:25 +05:30
< template v-if = "displayStageEvents" >
2021-04-17 20:07:23 +05:30
< component
2021-06-08 01:23:25 +05:30
: is = "selectedStage.component"
: stage = "selectedStage"
: items = "selectedStageEvents"
2021-04-17 20:07:23 +05:30
/ >
< / template >
< / template >
< / section >
< / div >
< / div >
< / div >
< / div >
< / div >
< / template >