@@ -9,9 +9,11 @@ import MenuItem from '../../components/menuItem';
99import PureRenderMixin from 'react-addons-pure-render-mixin' ;
1010import SelectedGroupStore from '../../stores/selectedGroupStore' ;
1111import { t , tn } from '../../locale' ;
12+ import { getShortVersion } from '../../utils' ;
1213
1314import CustomIgnoreCountModal from '../../components/customIgnoreCountModal' ;
1415import CustomIgnoreDurationModal from '../../components/customIgnoreDurationModal' ;
16+ import CustomResolutionModal from '../../components/customResolutionModal' ;
1517
1618const IgnoreActions = React . createClass ( {
1719 propTypes : {
@@ -243,6 +245,125 @@ const IgnoreActions = React.createClass({
243245 }
244246} ) ;
245247
248+ const ResolveActions = React . createClass ( {
249+ propTypes : {
250+ orgId : React . PropTypes . string . isRequired ,
251+ projectId : React . PropTypes . string . isRequired ,
252+ hasRelease : React . PropTypes . bool . isRequired ,
253+ latestRelease : React . PropTypes . object ,
254+ anySelected : React . PropTypes . bool . isRequired ,
255+ allInQuerySelected : React . PropTypes . bool . isRequired ,
256+ pageSelected : React . PropTypes . bool . isRequired ,
257+ onUpdate : React . PropTypes . func . isRequired ,
258+ query : React . PropTypes . string
259+ } ,
260+
261+ getInitialState ( ) {
262+ return {
263+ modal : false
264+ } ;
265+ } ,
266+
267+ onCustomResolution ( statusDetails ) {
268+ this . setState ( {
269+ modal : false
270+ } ) ;
271+ this . props . onUpdate ( {
272+ status : 'resolved' ,
273+ statusDetails : statusDetails
274+ } ) ;
275+ } ,
276+
277+ render ( ) {
278+ let { hasRelease, latestRelease, projectId, orgId} = this . props ;
279+ let extraDescription = null ;
280+ if ( this . state . allInQuerySelected ) {
281+ extraDescription = this . props . query
282+ ? ( < div >
283+ < p > { t ( 'This will apply to the current search query:' ) } </ p >
284+ < pre > { this . props . query } </ pre >
285+ </ div > )
286+ : ( < p className = "error" >
287+ < strong > { t ( 'This will apply to ALL issues in this project!' ) } </ strong >
288+ </ p > ) ;
289+ }
290+ let linkClassName = 'group-resolve btn btn-default btn-sm' ;
291+ let actionLinkProps = {
292+ onlyIfBulk : true ,
293+ disabled : ! this . props . anySelected ,
294+ selectAllActive : this . props . pageSelected ,
295+ extraDescription : extraDescription ,
296+ buttonTitle : t ( 'Ignore' ) ,
297+ confirmationQuestion : this . state . allInQuerySelected
298+ ? t ( 'Are you sure you want to ignore all issues matching this search query?' )
299+ : count =>
300+ tn (
301+ 'Are you sure you want to ignore this %d issue?' ,
302+ 'Are you sure you want to ignore these %d issues?' ,
303+ count
304+ ) ,
305+ confirmLabel : this . props . allInQuerySelected
306+ ? t ( 'Ignore all issues' )
307+ : count => tn ( 'Ignore %d selected issue' , 'Ignore %d selected issues' , count )
308+ } ;
309+ return (
310+ < div style = { { display : 'inline-block' } } >
311+ < CustomResolutionModal
312+ show = { this . state . modal }
313+ onSelected = { this . onCustomResolution }
314+ onCanceled = { ( ) => this . setState ( { modal : false } ) }
315+ orgId = { orgId }
316+ projectId = { projectId }
317+ />
318+ < div className = "btn-group" >
319+ < ActionLink
320+ onAction = { ( ) => this . props . onUpdate ( { status : 'resolved' } ) }
321+ className = { linkClassName }
322+ { ...actionLinkProps } >
323+ < span className = "icon-checkmark" style = { { marginRight : 5 } } />
324+ { t ( 'Resolve' ) }
325+ </ ActionLink >
326+ < DropdownLink
327+ key = "resolve-dropdown"
328+ caret = { true }
329+ className = { linkClassName }
330+ title = ""
331+ disabled = { ! this . props . anySelected } >
332+ < MenuItem header = { true } > { t ( 'Resolved In' ) } </ MenuItem >
333+ < MenuItem noAnchor = { true } >
334+ < ActionLink
335+ onAction = { ( ) =>
336+ this . props . onUpdate ( {
337+ status : 'resolved' ,
338+ statusDetails : { inNextRelease : true }
339+ } ) }
340+ { ...actionLinkProps } >
341+ { t ( 'The next release' ) }
342+ </ ActionLink >
343+ < ActionLink
344+ onAction = { ( ) =>
345+ this . props . onUpdate ( {
346+ status : 'resolved' ,
347+ statusDetails : {
348+ inRelease : latestRelease ? latestRelease . version : 'latest'
349+ }
350+ } ) }
351+ { ...actionLinkProps } >
352+ { latestRelease
353+ ? t ( 'The current release (%s)' , getShortVersion ( latestRelease . version ) )
354+ : t ( 'The current release' ) }
355+ </ ActionLink >
356+ < a onClick = { ( ) => hasRelease && this . setState ( { modal : true } ) } >
357+ { t ( 'Another version ...' ) }
358+ </ a >
359+ </ MenuItem >
360+ </ DropdownLink >
361+ </ div >
362+ </ div >
363+ ) ;
364+ }
365+ } ) ;
366+
246367const StreamActions = React . createClass ( {
247368 propTypes : {
248369 allResultsVisible : React . PropTypes . bool ,
@@ -254,7 +375,8 @@ const StreamActions = React.createClass({
254375 realtimeActive : React . PropTypes . bool . isRequired ,
255376 statsPeriod : React . PropTypes . string . isRequired ,
256377 query : React . PropTypes . string . isRequired ,
257- hasReleases : React . PropTypes . bool
378+ hasReleases : React . PropTypes . bool ,
379+ latestRelease : React . PropTypes . object
258380 } ,
259381
260382 mixins : [
@@ -263,6 +385,10 @@ const StreamActions = React.createClass({
263385 PureRenderMixin
264386 ] ,
265387
388+ getDefaultProps ( ) {
389+ return { hasReleases : false , latestRelease : null } ;
390+ } ,
391+
266392 getInitialState ( ) {
267393 return {
268394 datePickerActive : false ,
@@ -385,8 +511,6 @@ const StreamActions = React.createClass({
385511 render ( ) {
386512 // TODO(mitsuhiko): very unclear how to translate this
387513 let numIssues = SelectedGroupStore . getSelectedIds ( ) . size ;
388- let hasRelease = this . props . hasReleases ;
389- let releaseTrackingUrl = `/${ this . props . orgId } /${ this . props . projectId } /settings/release-tracking/` ;
390514 let extraDescription = null ;
391515 if ( this . state . allInQuerySelected ) {
392516 extraDescription = this . props . query
@@ -411,104 +535,17 @@ const StreamActions = React.createClass({
411535 checked = { this . state . pageSelected }
412536 />
413537 </ div >
414- < div className = "btn-group" >
415- < ActionLink
416- className = "btn btn-default btn-sm action-resolve"
417- disabled = { ! this . state . anySelected }
418- onAction = { this . onUpdate . bind ( this , { status : 'resolved' } ) }
419- buttonTitle = { t ( 'Resolve' ) }
420- extraDescription = { extraDescription }
421- confirmationQuestion = {
422- this . state . allInQuerySelected
423- ? t (
424- 'Are you sure you want to resolve all issues matching this search query?'
425- )
426- : count =>
427- tn (
428- 'Are you sure you want to resolve this %d issue?' ,
429- 'Are you sure you want to resolve these %d issues?' ,
430- count
431- )
432- }
433- confirmLabel = {
434- this . state . allInQuerySelected
435- ? t ( 'Resolve all issues' )
436- : count =>
437- tn (
438- 'Resolve %d selected issue' ,
439- 'Resolve %d selected issues' ,
440- count
441- )
442- }
443- tooltip = { t ( 'Set Status to Resolved' ) }
444- onlyIfBulk = { true }
445- selectAllActive = { this . state . pageSelected } >
446- < span className = "icon-checkmark" style = { { marginRight : 5 } } />
447- { t ( 'Resolve' ) }
448- </ ActionLink >
449- < DropdownLink
450- caret = { true }
451- className = "btn btn-default btn-sm action-resolve"
452- topLevelClasses = "resolve-dropdown"
453- disabled = { ! this . state . anySelected }
454- title = "" >
455- < MenuItem noAnchor = { true } >
456- { hasRelease
457- ? < ActionLink
458- disabled = { ! this . state . anySelected }
459- onAction = { this . onUpdate . bind ( this , {
460- status : 'resolvedInNextRelease'
461- } ) }
462- buttonTitle = { t ( 'Resolve' ) }
463- tooltip = ""
464- extraDescription = { extraDescription }
465- confirmationQuestion = {
466- this . state . allInQuerySelected
467- ? t (
468- 'Are you sure you want to resolve all issues matching this search query?'
469- )
470- : count =>
471- tn (
472- 'Are you sure you want to resolve this %d issue?' ,
473- 'Are you sure you want to resolve these %d issues?' ,
474- count
475- )
476- }
477- confirmLabel = {
478- this . state . allInQuerySelected
479- ? t ( 'Resolve all issues' )
480- : count =>
481- tn (
482- 'Resolve %d selected issue' ,
483- 'Resolve %d selected issues' ,
484- count
485- )
486- }
487- onlyIfBulk = { true }
488- selectAllActive = { this . state . pageSelected } >
489- < strong > { t ( 'Resolved in next release' ) } </ strong >
490- < div className = "help-text" >
491- { t (
492- 'Snooze notifications until this issue reoccurs in a future release.'
493- ) }
494- </ div >
495- </ ActionLink >
496- : < a
497- href = { releaseTrackingUrl }
498- className = "disabled tip"
499- title = { t (
500- 'Set up release tracking in order to use this feature.'
501- ) } >
502- < strong > { t ( 'Resolved in next release.' ) } </ strong >
503- < div className = "help-text" >
504- { t (
505- 'Snooze notifications until this issue reoccurs in a future release.'
506- ) }
507- </ div >
508- </ a > }
509- </ MenuItem >
510- </ DropdownLink >
511- </ div >
538+ < ResolveActions
539+ hasRelease = { this . props . hasReleases }
540+ latestRelease = { this . props . latestRelease }
541+ anySelected = { this . state . anySelected }
542+ onUpdate = { this . onUpdate }
543+ allInQuerySelected = { this . state . allInQuerySelected }
544+ pageSelected = { this . state . pageSelected }
545+ query = { this . props . query }
546+ orgId = { this . props . orgId }
547+ projectId = { this . props . projectId }
548+ />
512549 < IgnoreActions
513550 anySelected = { this . state . anySelected }
514551 onUpdate = { this . onUpdate }
0 commit comments