app/assets/javascripts/backbone/views/bid_review_view.js.coffee
ProcureIo.Backbone.BidReviewSubviews = []
# Bids <thead>
ProcureIo.Backbone.BidReviewSubviews.push Backbone.View.extend
el: ".subview-bids-thead"
initialize: ->
@listenTo @options.parentView.pageOptions, 'change:keyFields', @render
@listenTo @options.parentView.router.filterOptions, 'change', @render
render: ->
@$el.html JST['bid_review/thead']({parentView: @options.parentView})
@$el.closest("table").resizableColumns()
# Field chooser
ProcureIo.Backbone.BidReviewSubviews.push Backbone.View.extend
el: ".subview-field-chooser"
initialize: ->
@listenTo @options.parentView.pageOptions, "change:keyFields", @render
render: ->
fieldSelected = (id) =>
_.find @options.parentView.pageOptions.get('keyFields'), (kf) ->
kf == id
@$el.html JST['bid_review/field_chooser']
responseFields: @options.parentView.options.project.response_fields
fieldSelected: fieldSelected
# Actions (label, dismiss, award)
ProcureIo.Backbone.BidReviewSubviews.push Backbone.View.extend
el: ".subview-actions-wrapper"
initialize: ->
@listenTo @options.parentView.labels, "add", @render
@listenTo @options.parentView.labels, "remove", @render
@listenTo @options.parentView.labels, "change", @render
render: ->
@$el.html JST['bid_review/actions']
labels: @options.parentView.labels
abilities: @options.parentView.abilities
rivets.bind @$el,
pageOptions: @options.parentView.pageOptions
filterOptions: @options.parentView.router.filterOptions
# Sidebar filters (open, dismissed, awarded)
ProcureIo.Backbone.BidReviewSubviews.push Backbone.View.extend
el: ".subview-sidebar-filter"
render: ->
@$el.html JST['bid_review/sidebar_filter']()
rivets.bind @$el,
pageOptions: @options.parentView.pageOptions
filterOptions: @options.parentView.router.filterOptions
counts: @options.parentView.counts
# Label filters
ProcureIo.Backbone.BidReviewSubviews.push Backbone.View.extend
el: ".subview-label-filter"
initialize: ->
return unless @$el.length > 0
@listenTo @options.parentView.labels, "add", @addOneLabel
createLabel: (e, $el) ->
labelName = $el.find('input[name="label[name]"]').val()
labelColor = $el.find('input[name="label[color]"]').val().replace(/^\#/, '') || ProcureIo.Backbone.DEFAULT_LABEL_COLOR
labelExists = @options.parentView.labels.existingNames().indexOf(labelName.toLowerCase()) != -1
return if !labelName or labelExists
@options.parentView.labels.create
name: labelName
color: labelColor
@render() # resets everything
showColors: ->
$(".color-wrapper").removeClass 'hide'
selectSwatch: (e, $el) ->
return if $el.hasClass 'selected'
$el.siblings().removeClass 'selected'
$el.addClass 'selected'
@$el.find("#new-label-form .custom-color-input").val($el.data('color'))
typingCustomColor: (e) ->
@$el.find(".color-swatches .swatch.selected").removeClass 'selected'
$("#new-label-form .custom-color-input").val($(e.target).val())
render: ->
@$el.html JST['bid_review/label_filter']
abilities: @options.parentView.options.abilities
@resetLabels()
resetLabels: ->
$("#labels-list").html('')
@addAllLabels()
addAllLabels: ->
@options.parentView.labels.each @addOneLabel, @
addOneLabel: (label) ->
view = new ProcureIo.Backbone.BidReviewLabelView
model: label
parentView: @
el = view.render().el
@$el.find("#labels-list").append(el)
rivets.bind el,
pageOptions: @options.parentView.pageOptions
filterOptions: @options.parentView.router.filterOptions
# Label admin
ProcureIo.Backbone.BidReviewSubviews.push Backbone.View.extend
el: ".subview-label-admin"
initialize: ->
return unless @$el.length > 0
@listenTo @options.parentView.labels, "add", @addOneLabel
render: ->
@$el.html JST['bid_review/label_admin_list']()
@resetLabels()
resetLabels: ->
$("#labels-admin-list").html('')
@addAllLabels()
addAllLabels: ->
@options.parentView.labels.each @addOneLabel, @
addOneLabel: (label) ->
view = new ProcureIo.Backbone.BidReviewLabelAdminView
model: label
parentView: @
@$el.find("#labels-admin-list").append(view.render().el)
ProcureIo.Backbone.BidReviewLabelAdminView = Backbone.View.extend
tagName: "li"
initialize: ->
@listenTo @model, "destroy", @remove
@listenTo @model, "change", @render
showEditPane: (e) ->
if @$el.data('popover')
return @$el.popover 'toggle'
@$el.siblings().popover 'destroy'
@$el.popover
content: (new ProcureIo.Backbone.BidReviewLabelEditView({label: @model, parentView: @})).render().el
html: true
animation: false
trigger: "manual"
template: """
<div class="popover"><div class="arrow"></div><div class="popover-inner"><div class="popover-content"></div></div></div
"""
@$el.popover 'show'
e.preventDefault()
render: ->
@$el.html JST['bid_review/label_admin']
model: @model
return @
clear: ->
@model.destroy()
ProcureIo.Backbone.BidReviewLabelEditView = Backbone.View.extend
initialize: ->
@label = @options.label
@parentView = @options.parentView
@listenTo @label, "destroy", @remove
render: ->
@$el.html JST['bid_review/label_edit']({label: @label})
rivets.bind(@$el, {label: @label})
@$el.find(".swatch").removeClass 'selected'
if @$el.find(".swatch[data-color=\"#{@label.get('color')}\"]").length > 0
@$el.find(".swatch[data-color=\"#{@label.get('color')}\"]").addClass 'selected'
else
@$el.find(".custom-color-input").removeClass 'hide'
@$el.find(".custom-color-input").val @label.get('color')
self = @
@$el.find(".custom-color-input").on "input", ->
self.$el.find(".color-swatches .swatch.selected").removeClass 'selected'
self.$el.find("[data-rv-value=\"label\.color\"]").val($(@).val()).trigger('change') # trigger rivets update
@$el.find(".color-swatches .swatch").on "click", ->
if !$(@).hasClass 'selected'
$(@).siblings().removeClass 'selected'
$(@).addClass 'selected'
self.$el.find(".custom-color-input").val('')
self.$el.find("[data-rv-value=\"label\.color\"]").val($(@).data('color')).trigger('change') # trigger rivets update
return @
saveLabel: (e) ->
@removePopover()
@label.save()
removeLabel: ->
@removePopover()
@label.destroy()
removePopover: ->
@parentView.$el.popover 'destroy'
ProcureIo.Backbone.BidReviewLabelView = Backbone.View.extend
tagName: "li"
initialize: ->
@listenTo @model, "destroy", @remove
@listenTo @model, "change", @render
@listenTo @options.parentView.options.parentView.router.filterOptions, "change:label", @addOrRemoveActiveClass
render: ->
@$el.html JST['bid_review/label']
label: @model
rivets.bind @$el,
counts: @options.parentView.options.parentView.counts
return @
clear: ->
@model.destroy()
addOrRemoveActiveClass: ->
if @options.parentView.options.parentView.router.filterOptions.get('label') == @model.get('name')
@$el.addClass('active')
else
@$el.removeClass('active')
ProcureIo.Backbone.BidReviewBidView = Backbone.View.extend
tagName: "tr"
className: "bid-tr"
events:
"mouseenter": "selectBid"
"click": "openBidUnlessClickingOnAnotherLink"
initialize: ->
@parentView = @options.parentView
@listenTo @model, "destroy", @remove
@listenTo @model, "change", @render
@listenTo @model, "sync", @checkForRefetch
checkForRefetch: ->
if (@parentView.router.filterOptions.get("status") == "open" && (@model.get("dismissed_at") || @model.get("awarded_at"))) ||
(@parentView.router.filterOptions.get("status") == "dismissed" && !@model.get("dismissed_at")) ||
(@parentView.router.filterOptions.get("status") == "awarded" && !@model.get("awarded_at"))
@parentView.refetch()
render: ->
@$el.html JST['bid_review/bid']
bid: @model
parentView: @parentView
rivets.bind @$el,
bid: @model
@$el[if !@model.get("read") then "addClass" else "removeClass"]('bid-tr-unread')
@$el[if @model.get("checked") then "addClass" else "removeClass"]('bid-tr-checked')
@$el.find(".rating-select").raty
score: ->
$(@).attr('data-score')
click: (score) =>
@model.set('rating', score)
@model.save()
return @
clear: ->
@model.destroy()
openBidUnlessClickingOnAnotherLink: (e) ->
return if $(e.target).closest("a, .no-link").length > 0
@openBid()
openBid: (e) ->
return if e && e.metaKey
return @$modal.modal('show') if @$modal?
e.preventDefault() if e
if !@bidOpened
@bidOpened = true
$.ajax
url: "/projects/#{@parentView.project.id}/bids/#{@model.id}/read_notifications.json"
type: "POST"
@toggleRead() unless @model.get('read')
@$modal = $("""
<div class="modal container hide modal-fullscreen" tabindex="-1">
<div class="modal-body">
<div class="modal-bid-view-wrapper"></div>
<div class="modal-bid-view-reviews-wrapper"></div>
<h4 class="modal-comments-header">#{I18n.t('g.comments')}</h4>
<div class="row">
<div class="span8">
<div class="modal-comments-view-wrapper"></div>
</div>
</div>
</div>
</div>
""").appendTo("body")
@modalBidView = new ProcureIo.Backbone.BidPageView
bid: @model
project: @parentView.project
el: @$modal.find(".modal-bid-view-wrapper")
abilities: @parentView.abilities
if @parentView.abilities.commentOn
@modalCommentsView = new ProcureIo.Backbone.CommentPageView
commentableType: "Bid"
commentableId: @model.id
el: @$modal.find(".modal-comments-view-wrapper")
else
@$el.find(".modal-comments-header").hide() # this is hacky.
if @parentView.abilities.seeAllReviews
@modalReviewsView = new ProcureIo.Backbone.BidPageReviewsView
project_id: @parentView.project.id
bid_id: @model.id
el: @$modal.find(".modal-bid-view-reviews-wrapper")
@$modal.modal('show')
@$modal.on "hidden", =>
@modalBidView.remove()
@modalCommentsView?.remove()
@modalReviewsView?.remove()
@$modal.remove()
@$modal = undefined
toggleStarred: ->
@model.set 'starred', (if @model.get('starred') then false else true)
@save()
toggleRead: ->
@model.set 'read', (if @model.get('read') then false else true)
@save()
save: ->
@model.save()
selectBid: ->
if ProcureIo.BidsOnMouseoverSelect
$(".bid-tr-selected").removeClass("bid-tr-selected")
@$el.addClass 'bid-tr-selected'
ProcureIo.Backbone.BidReviewPage = Backbone.View.extend
el: "#bid-review-page"
initialize: ->
# Hack for hotkey selection
ProcureIo.BidsOnMouseoverSelect = true
@project = @options.project
@abilities = @options.abilities
@keyFieldStorageKey = "project#{@project.id}-keyfields"
@sidebarStorageKey = "project#{@project.id}-sidebar"
@collection = new ProcureIo.Backbone.BidList()
# @todo investigate
@collection.baseUrl = "/projects/#{@project.id}/bids"
@collection.url = "#{@collection.baseUrl}.json"
@collection.bind 'add', @addOne, @
@collection.bind 'reset', @reset, @
@collection.bind 'reset', @doneLoading, @
@collection.bind 'reset', @setCounts, @
@labels = new ProcureIo.Backbone.LabelList()
@labels.url = "/projects/#{@options.project.id}/labels"
@labels.reset(@project.labels)
@counts = new Backbone.Model
@pageOptions = new Backbone.Model
keyFields: @project.key_fields
@setKeyFields()
@router = new ProcureIo.Backbone.SearchRouter @collection,
status: "open"
sort: "name"
direction: "asc"
@subviews = {}
@render()
for subview in _.union ProcureIo.Backbone.BidReviewSubviews, ProcureIo.Backbone.PaginationView
new subview({parentView: @}).render()
if store.get(@sidebarStorageKey) == 'collapsed'
@collapseSidebarImmediately()
if @options.bootstrapData
@router.setParamsFromUrl()
@collection.reset(@options.bootstrapData, {parse: true})
bootstrapped = true
Backbone.history.start
pushState: true
silent: bootstrapped?
getLabel: (label_id) ->
@labels.find ( (label) -> label.get('id') == label_id )
reset: ->
$("#bids-tbody").html('')
@addAll()
if @collection.models.length > 0
$("#bids-tbody").show()
$("#no-bids-tbody").hide()
else
$("#bids-tbody").hide()
$("#no-bids-tbody").show()
render: ->
@$el.html JST['bid_review/page']
abilities: @abilities
project: @project
rivets.bind @$el,
pageOptions: @pageOptions
filterOptions: @router.filterOptions
addOne: (bid) ->
view = new ProcureIo.Backbone.BidReviewBidView({model: bid, parentView: @})
@listenTo bid, "change:checked", ( => @seeIfBidsChecked() )
@$el.find("#bids-tbody").append(view.render().el)
seeIfBidsChecked: ->
if @collection.find ((b) -> b.get('checked') == true)
@pageOptions.set('bidsChecked', true)
else
@pageOptions.unset('bidsChecked')
addAll: ->
@collection.each @addOne, @
updateFilter: ProcureIo.Backbone.Mixins.updateFilter
checkedBidIds: (e) ->
_.map @collection.where({checked:true}), (b) -> b.attributes.id
dismissCheckedBids: (e) ->
options =
dismissal_message: $(e.target).find(".js-dismissal-message").val()
show_dismissal_message_to_vendor: $(e.target).find(".js-show-dismissal-message-to-vendor").is(":checked")
@sendBatchAction('dismiss', @checkedBidIds(), options)
awardCheckedBids: (e) ->
options =
award_message: $(e.target).find(".js-award-message").val()
@sendBatchAction('award', @checkedBidIds(), options)
labelCheckedBids: (e, $el, labelId) ->
@sendBatchAction('label', @checkedBidIds(), {label_id: labelId})
toggleLabelAdmin: ->
@$el.toggleClass 'editing-labels'
sendBatchAction: (action, ids, options = {}) ->
$("#bid-review-page").addClass 'loading'
$.ajax
url: "#{@collection.baseUrl}/batch"
type: "PUT"
data:
ids: ids
bid_action: action
options: options
success: =>
@refetch()
doneLoading: ->
$("#bid-review-page").removeClass 'loading'
getResponseField: (id) ->
_.find @project.response_fields, (rf) ->
rf.id == id
setKeyFields: ->
if (storedKeyFields = store.get(@keyFieldStorageKey))
@pageOptions.set 'keyFields', @sortedKeyFields(storedKeyFields)
else
@setDefaultKeyFields()
@storeKeyFields()
setDefaultKeyFields: ->
keyFields = []
for responseField in @project.response_fields
keyFields.push responseField.id
break if keyFields.length == 2
keyFields.unshift 'rating', 'comments', 'name'
@pageOptions.set 'keyFields', keyFields
storeKeyFields: ->
store.set(@keyFieldStorageKey, @pageOptions.get('keyFields'))
sortedKeyFields: (keyFields) ->
sortedKeyFields = []
# sort these first
for i in ['rating', 'comments', 'name']
if _.contains keyFields, i
sortedKeyFields.push i
# sort other fields in the order they came in
for responseField in @project.response_fields
sortedKeyFields.push(responseField.id) if _.contains(keyFields, responseField.id)
sortedKeyFields
toggleKeyField: (e) ->
id = $(e.target).data('response-field-id')
if _.contains @pageOptions.get('keyFields'), id
newKeyFields = _.without(@pageOptions.get('keyFields'), id)
else
newKeyFields = _.union(@pageOptions.get('keyFields'), [id])
@pageOptions.set 'keyFields', @sortedKeyFields(newKeyFields)
@storeKeyFields()
@collection.each (b) ->
b.trigger 'change'
refetch: ->
$("#bid-review-page").addClass 'loading'
@collection.fetch
data: @router.filterOptions.toJSON()
collapseSidebarImmediately: ->
$sidebar = @$el.find(".sidebar-wrapper")
$rightSideSpan = @$el.find(".right-side-span")
$sidebar.removeClass('span3').addClass('span1')
$rightSideSpan.removeClass('span9').addClass('span11')
$sidebar.toggleClass 'sidebar-collapsed'
@$el.find("[data-backbone-click=toggleCollapseSidebar]").toggleText()
@pageOptions.set('sidebarCollapsed', true)
toggleCollapseSidebar: ->
$sidebar = @$el.find(".sidebar-wrapper")
$rightSideSpan = @$el.find(".right-side-span")
if $sidebar.hasClass 'sidebar-collapsed'
# restore
$sidebar.css({width: 70})
$sidebar.removeClass('span1').addClass('span3')
$rightSideSpan.removeClass('span11').addClass('span9')
$sidebar.animate
width: 270
, 300
setTimeout ->
$sidebar.toggleClass 'sidebar-collapsed'
, 200
store.remove(@sidebarStorageKey)
@pageOptions.unset('sidebarCollapsed')
else
# collapse
$preHides = $sidebar.find(".badge, #new-label-form")
$preHides.addClass('hide')
$sidebar.toggleClass 'sidebar-collapsed'
$sidebar.animate
width: 70
, 300, ->
$sidebar.css({width: ''})
$sidebar.removeClass('span3').addClass('span1')
$rightSideSpan.removeClass('span9').addClass('span11')
$preHides.removeClass('hide')
store.set(@sidebarStorageKey, 'collapsed')
@pageOptions.set('sidebarCollapsed', true)
submitSearch: (e) ->
@router.navigate @router.filteredHref({page: 1}), {trigger: true}
setCounts: ->
for k, v of @collection.meta.counts
@counts.set(k, v)
viewFilteredEmails: (e) ->
$modal = $("""
<div class="modal" tabindex="-1">
<div class="modal-body">
<pre class="js-email-target">Loading...</pre>
</div>
</div>
""").appendTo("body").modal('show')
$.getJSON "/projects/#{@project.id}/bids/emails?#{$.param(@router.filterOptions.toJSON())}", (data) ->
$modal.find(".js-email-target").text(data)