Bugzilla – Attachment 60927 Details for
Bug 17297
Give librarians the ability to add messages to an item
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 17297 - Add React based item message editor
Bug-17297---Add-React-based-item-message-editor.patch (text/plain), 14.63 KB, created by
Kyle M Hall (khall)
on 2017-03-08 18:14:10 UTC
(
hide
)
Description:
Bug 17297 - Add React based item message editor
Filename:
MIME Type:
Creator:
Kyle M Hall (khall)
Created:
2017-03-08 18:14:10 UTC
Size:
14.63 KB
patch
obsolete
>From c4590e1000e1844db4def1f94683a9e79dc94f47 Mon Sep 17 00:00:00 2001 >From: Kyle M Hall <kyle@bywatersolutions.com> >Date: Tue, 13 Sep 2016 16:08:07 +0000 >Subject: [PATCH] Bug 17297 - Add React based item message editor > >Signed-off-by: Josef Moravec <josef.moravec@gmail.com> >--- > catalogue/moredetail.pl | 9 +- > koha-tmpl/intranet-tmpl/prog/css/staff-global.css | 96 +++++++------ > .../prog/en/modules/catalogue/moredetail.tt | 11 ++ > .../intranet-tmpl/prog/js/public/moredetail.js | 150 +++++++++++++++++++++ > 4 files changed, 220 insertions(+), 46 deletions(-) > create mode 100644 koha-tmpl/intranet-tmpl/prog/js/public/moredetail.js > >diff --git a/catalogue/moredetail.pl b/catalogue/moredetail.pl >index 125b103..b443b20 100755 >--- a/catalogue/moredetail.pl >+++ b/catalogue/moredetail.pl >@@ -30,14 +30,15 @@ use C4::Acquisition; > use C4::Output; > use C4::Auth; > use C4::Serials; >-use C4::Members; # to use GetMember >-use C4::Search; # enabled_staff_search_views >+use C4::Members; # to use GetMember >+use C4::Search; # enabled_staff_search_views > use C4::Reserves qw(GetReservesFromBiblionumber); > > use Koha::Acquisition::Booksellers; > use Koha::AuthorisedValues; > use Koha::DateUtils; > use Koha::Items; >+use Koha::Item::Messages; > use Koha::Patrons; > > my $query=new CGI; >@@ -133,8 +134,10 @@ foreach ( keys %{$data} ) { > } > > ($itemnumber) and @items = (grep {$_->{'itemnumber'} == $itemnumber} @items); >+my $item_messages; > foreach my $item (@items){ > $item->{object} = Koha::Items->find( $item->{itemnumber} ); >+ $item_messages->{ $item->{itemnumber} } = ( Koha::Item::Messages->search( { itemnumber => $item->{itemnumber} } )->unblessed ); > $item->{'collection'} = $ccodes->{ $item->{ccode} } if ($ccodes); > $item->{'itype'} = $itemtypes->{ $item->{'itype'} }->{'translated_description'}; > $item->{'replacementprice'} = sprintf( "%.2f", $item->{'replacementprice'} ); >@@ -226,6 +229,7 @@ $template->param( > z3950_search_params => C4::Search::z3950_search_args(GetBiblioData($biblionumber)), > subtitle => $subtitle, > hidepatronname => $hidepatronname, >+ item_messages => $item_messages, > ); > $template->param(ONLY_ONE => 1) if ( $itemnumber && $showncount != @items ); > $template->{'VARS'}->{'searchid'} = $query->param('searchid'); >@@ -261,6 +265,7 @@ $template->param (countdeletedorders => $count_deletedorders_using_biblio); > my $holds = GetReservesFromBiblionumber({ biblionumber => $biblionumber, all_dates => 1 }); > my $holdcount = scalar( @$holds ); > $template->param( holdcount => scalar ( @$holds ) ); >+warn Data::Dumper::Dumper( $item_messages ); > > output_html_with_http_headers $query, $cookie, $template->output; > >diff --git a/koha-tmpl/intranet-tmpl/prog/css/staff-global.css b/koha-tmpl/intranet-tmpl/prog/css/staff-global.css >index 6b22201..2dd6f47 100644 >--- a/koha-tmpl/intranet-tmpl/prog/css/staff-global.css >+++ b/koha-tmpl/intranet-tmpl/prog/css/staff-global.css >@@ -75,12 +75,12 @@ h3 { > } > > h4 { >- font-size : 116%; >+ font-size : 116%; > font-weight : bold; > } > > h5 { >- font-size : 100%; >+ font-size : 100%; > font-weight : bold; > } > >@@ -614,7 +614,7 @@ div.yui-b fieldset.brief fieldset legend { > margin-left: 20px; > } > >-fieldset.rows { >+fieldset.rows { > border-width : 1px; > border:2px solid #b9d8d9; > float : left; >@@ -622,7 +622,7 @@ font-size : 90%; > clear : left; > margin: .9em 0 0 0; > padding: 0; >-width: 100%; >+width: 100%; > } > > fieldset.rows.unselected { >@@ -631,17 +631,17 @@ fieldset.rows.unselected { > border: 0; > } > >-fieldset.rows legend { >+fieldset.rows legend { > margin-left: 1em; > font-weight: bold; > font-size : 110%; > } > >-fieldset.rows label, fieldset.rows span.label { >- float: left; >+fieldset.rows label, fieldset.rows span.label { >+ float: left; > font-weight : bold; >- width: 6em; >- margin-right: 1em; >+ width: 6em; >+ margin-right: 1em; > text-align: right; > } > >@@ -652,8 +652,8 @@ fieldset.rows fieldset { > padding : .3em; > } > >-.yui-b fieldset.rows label, .yui-b fieldset.rows span.label { >- width: 9em; >+.yui-b fieldset.rows label, .yui-b fieldset.rows span.label { >+ width: 9em; > } > > .yui-b fieldset.rows td label, .yui-b fieldset.rows td span.label { >@@ -664,8 +664,8 @@ fieldset.rows fieldset { > margin-left : 10.5em; > } > >-.yui-u fieldset.rows label, .yui-u fieldset.rows span.label { >- width: 10em; >+.yui-u fieldset.rows label, .yui-u fieldset.rows span.label { >+ width: 10em; > } > > .yui-u fieldset.rows div.hint { >@@ -674,20 +674,20 @@ fieldset.rows fieldset { > > fieldset.rows.left label, fieldset.rows.left span.label { > width : 8em; >- text-align : left; >+ text-align : left; > } > >-fieldset.rows ol { >-padding: 1em 1em 0 1em; >+fieldset.rows ol { >+padding: 1em 1em 0 1em; > list-style-type: none; > } > >-fieldset.rows li { >+fieldset.rows li { > float : left; > clear : left; > padding-bottom: 1em; > list-style-type: none; >-width: 100%; >+width: 100%; > } > > fieldset.rows.left li { >@@ -783,17 +783,17 @@ div.rows+div.rows { > div.rows { > float : left; > clear : left; >-margin: 0 0 0 0; >+margin: 0 0 0 0; > padding: 0; >-width: 100%; >+width: 100%; > } > > >-div.rows span.label { >- float: left; >+div.rows span.label { >+ float: left; > font-weight : bold; > width: 9em; >- margin-right: 1em; >+ margin-right: 1em; > text-align: left; > padding-top: 0; > } >@@ -802,8 +802,8 @@ div.rows span.label { > white-space: normal; > } > >-div.rows ol { >-padding: .5em 1em 0 0; >+div.rows ol { >+padding: .5em 1em 0 0; > list-style-type: none; > } > >@@ -818,7 +818,7 @@ clear : left; > padding-bottom: .2em; > padding-top: .1em; > list-style-type: none; >-width: 100%; >+width: 100%; > } > > a.cancel { >@@ -946,8 +946,8 @@ ol.bibliodetails span.label { > border-right : 1px solid #E8E8E8; > float : left; > font-weight: bold; >- width: 12em; >- margin-right: 1em; >+ width: 12em; >+ margin-right: 1em; > } > > div.listgroup { >@@ -966,6 +966,14 @@ div.listgroup input { > font-size: 80%; > } > >+div.listgroup ol li span.label i { >+ margin-right : .5em; >+} >+ >+div.listgroup ol li button,a { >+ margin-left : .5em; >+} >+ > div.sysprefs h3 { > margin : .2em 0 .2em .4em; > } >@@ -1035,7 +1043,7 @@ span.expired { > } > > div.help { >- margin: .9em 0 0 0; >+ margin: .9em 0 0 0; > } > > .blocker { >@@ -1092,12 +1100,12 @@ div.first fieldset { > .dialog table td { > text-align : left; > } >- >+ > .dialog h2, .dialog h3, .dialog h4 { > margin : auto; > text-align : center; > } >- >+ > .dialog input { > background-color : #FFF; > border : 1px solid #bcbcbc; >@@ -1252,11 +1260,11 @@ div.note i.fa-exclamation { > div.results { > padding : .7em 0; > } >- >+ > .accesskey { > text-decoration : underline; > } >- >+ > label.required, > span.required { > color : #C00; >@@ -1388,7 +1396,7 @@ h1#logo a { > } > > #closewindow a { >- font-weight : bold; >+ font-weight : bold; > } > > .patroninfo+#menu { >@@ -1421,10 +1429,10 @@ li.email { > } > > .clearfix:after { >- content: "."; >- display: block; >- height: 0; >- clear: both; >+ content: "."; >+ display: block; >+ height: 0; >+ clear: both; > visibility: hidden; > } > >@@ -1664,7 +1672,7 @@ input.warning { > } > > #displayexample { >- padding: 5px; >+ padding: 5px; > margin-bottom: 10px; > background-color: #CCCCCC; > } >@@ -1781,7 +1789,7 @@ span.permissiondesc { > > .results_summary { > display: block; >- font-size : 85%; >+ font-size : 85%; > color: #707070; > padding : 0 0 .5em 0; > } >@@ -1886,10 +1894,10 @@ fieldset.rows+h3 {clear:both;padding-top:.5em;} > } > > .m880 { >- display:block; >- text-align:right; >- float:right; >- width:50%; >+ display:block; >+ text-align:right; >+ float:right; >+ width:50%; > padding-left:20px > } > >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/moredetail.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/moredetail.tt >index b04fd39..4b4df45 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/moredetail.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/moredetail.tt >@@ -1,5 +1,7 @@ >+[% USE To %] > [% USE Koha %] > [% USE Branches %] >+[% USE AuthorisedValues %] > [% INCLUDE 'doc-head-open.inc' %] > <title>Koha › Catalog › Item details for [% title | html %] [% FOREACH subtitl IN subtitle %] [% subtitl.subfield | html %][% END %]</title> > [% INCLUDE 'doc-head-close.inc' %] >@@ -13,8 +15,16 @@ > //<![CDATA[ > var browser = KOHA.browser('[% searchid %]', parseInt('[% biblionumber %]', 10)); > browser.show(); >+ >+ var authorised_values = [% To.json( AuthorisedValues.Get('ITEM_MESSAGE_TYPE') ) %] >+ var item_messages = [% To.json( item_messages ) %]; >+ >+ var CANCEL = _("Cancel"); >+ var ADD_MESSAGE = _("Add message"); >+ var ARE_YOU_SURE = _("Are you sure you want to delete the following message: "); > //]]> > </script> >+<script type="text/javascript" src="[% interface %]/[% theme %]/js/app/moredetail.js"></script> > </head> > <body id="catalog_moredetail" class="catalog"> > [% USE KohaDates %] >@@ -199,6 +209,7 @@ > [% END %] > > </ol></div> >+ <div id="item-messages-[% ITEM_DAT.itemnumber %]" class="item-messages"></div> > <div class="listgroup"><h4>History</h4> > <ol class="bibliodetails"> > >diff --git a/koha-tmpl/intranet-tmpl/prog/js/public/moredetail.js b/koha-tmpl/intranet-tmpl/prog/js/public/moredetail.js >new file mode 100644 >index 0000000..80ab376 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/js/public/moredetail.js >@@ -0,0 +1,150 @@ >+var React = require('react'); >+var ReactDOM = require('react-dom'); >+ >+var av_descriptions = []; >+$( document ).ready(function() { >+ for (var i = 0; i < authorised_values.length; i++) { >+ var av = authorised_values[i]; >+ av_descriptions[av.authorised_value] = av.lib; >+ } >+ >+ $('.item-messages').each(function() { >+ var itemnumber = parseInt( $(this).attr('id').split('item-messages-')[1] ); >+ ReactDOM.render( >+ <ItemMessages itemnumber={itemnumber} messages={item_messages[itemnumber]}/>, >+ this >+ ); >+ }); >+}); >+ >+var ItemMessages = React.createClass({ >+ propTypes: { >+ messages: React.PropTypes.array.isRequired, >+ itemnumber: React.PropTypes.number.isRequired, >+ }, >+ getInitialState: function() { >+ return { messages: this.props.messages }; >+ }, >+ addMessage: function(message) { >+ this.setState(function(state) { >+ var newData = state.messages.slice(); >+ newData.push( message ); >+ return {messages: newData}; >+ }); >+ }, >+ removeMessage: function(index) { >+ var message = this.state.messages[index]; >+ var itemMessage = this; >+ $.ajax({ >+ url: '/api/v1/items/' + message.itemnumber + '/messages/' + message.item_message_id, >+ type: 'DELETE', >+ success: function( result ) { >+ itemMessage.setState(function(state) { >+ var newData = state.messages.slice(); >+ newData.splice(index, 1); >+ return {messages: newData}; >+ }); >+ } >+ }); >+ }, >+ render: function () { >+ var item_messages = this; >+ return <div className="listgroup"> >+ <h4>Messages</h4> >+ <ol className="bibliodetails"> >+ {this.state.messages.map(function(message, index) { >+ return <ItemMessage key={message.item_message_id} message={message} index={index} onRemove={item_messages.removeMessage} /> >+ })} >+ <ItemMessageCreator itemnumber={this.props.itemnumber} onAdd={item_messages.addMessage}/> >+ </ol> >+ </div>; >+ } >+}); >+ >+var ItemMessage = React.createClass({ >+ propTypes: { >+ message: React.PropTypes.object.isRequired, >+ index: React.PropTypes.number.isRequired, >+ onRemove: React.PropTypes.func.isRequired, >+ }, >+ removeMessage: function() { >+ if ( confirm( ARE_YOU_SURE + this.props.message.message ) ) { >+ this.props.onRemove(this.props.index); >+ } >+ }, >+ render: function () { >+ return <li> >+ <span className="label"> >+ <i className="fa fa-minus-circle" onClick={this.removeMessage}></i> >+ {av_descriptions[this.props.message.type]} >+ </span> >+ {this.props.message.message} >+ </li> >+ } >+}); >+ >+var ItemMessageCreator = React.createClass({ >+ propTypes: { >+ itemnumber: React.PropTypes.number.isRequired, >+ onAdd: React.PropTypes.func.isRequired, >+ }, >+ getInitialState: function () { >+ return { >+ message: "", >+ type: authorised_values[0].authorised_value, >+ }; >+ }, >+ addMessage: function() { >+ var self = this; >+ var item_message = { >+ type: this.state.type, >+ message: this.state.message, >+ }; >+ $.ajax({ >+ type: 'POST', >+ url: '/api/v1/items/' + this.props.itemnumber + '/messages', >+ data: JSON.stringify(item_message), >+ success: function(data) { >+ self.cancelMessage(); >+ self.props.onAdd(data); >+ }, >+ error: function (xhr, ajaxOptions, thrownError) { >+ console.log(xhr.responseText); >+ console.log(thrownError); >+ }, >+ }); >+ }, >+ cancelMessage: function() { >+ this.setState( this.getInitialState ); >+ return false; >+ }, >+ handleTypeChange: function(e) { >+ this.setState({ type: e.target.value }); >+ }, >+ handleContentChange: function(e) { >+ this.setState({ message: e.target.value }); >+ }, >+ render: function () { >+ var options = []; >+ for (var i = 0; i < authorised_values.length; i++) { >+ var av = authorised_values[i]; >+ options.push( >+ <option key={i} value={av.authorised_value}>{av.lib}</option> >+ ); >+ } >+ >+ return <li> >+ <span className="label"> >+ <select value={this.state.type} onChange={this.handleTypeChange}> >+ {options} >+ </select> >+ </span> >+ <input className="input-xlarge" type="text" value={this.state.message} onChange={this.handleContentChange} /> >+ <button className="submit" onClick={this.addMessage}> >+ <i className="fa fa-plus-circle"></i> >+ {ADD_MESSAGE} >+ </button> >+ <a href="javascript:void(0);" onClick={this.cancelMessage}>{CANCEL}</a> >+ </li> >+ } >+}); >-- >2.1.4
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 17297
:
55544
|
55545
|
55546
|
60861
|
60862
|
60863
|
60926
| 60927 |
60928