Line 0
Link Here
|
|
|
1 |
"use strict"; |
2 |
|
3 |
/* global __, Koha, fetch */ |
4 |
|
5 |
import _ from "lodash"; |
6 |
import React from "react"; |
7 |
|
8 |
import { DropDown, GroupedDropDown } from "./common"; |
9 |
import { RULE_KINDS, KINDS_BY_SCOPE } from "./core"; |
10 |
import rules from "./rules"; |
11 |
import RuleCell from "./rule-cell"; |
12 |
|
13 |
function AllHiddenHeader() { |
14 |
return <th className="all-hidden" title={__("All rules are currently hidden; to see them, change your filter in the toolbar.")}> |
15 |
{__("All rules hidden")} |
16 |
</th>; |
17 |
} |
18 |
|
19 |
class RuleEditor extends React.Component { |
20 |
constructor( props ) { |
21 |
super( props ); |
22 |
|
23 |
this.state = { |
24 |
visibleChoices: _.union( [ "" ], _.sortBy( this._configuredChoices, choice => this._allChoices[choice] ) ), |
25 |
visibleKinds: _.uniq( |
26 |
KINDS_BY_SCOPE[this.scopeName] |
27 |
.filter( kind => kind.showByDefault ) |
28 |
.map( kind => kind.name ) |
29 |
.concat( this._ownRules.map( rule => rule.rule_name ) ) |
30 |
), |
31 |
}; |
32 |
} |
33 |
|
34 |
get branchcode() { |
35 |
return this.props.branch ? this.props.branch.branchcode : ""; |
36 |
} |
37 |
|
38 |
get categorycode() { |
39 |
// We have to handle both the case where there's never a categorycode and where the category |
40 |
// is unset (default rules). |
41 |
if ( this.props.category === null ) { |
42 |
return ""; |
43 |
} else if ( this.props.category === undefined ) { |
44 |
return undefined; |
45 |
} else { |
46 |
return this.props.category.categorycode; |
47 |
} |
48 |
} |
49 |
|
50 |
get _ownRules() { |
51 |
return rules.getAllDefined( { branchcode: this.branchcode, categorycode: this.categorycode, scope_name: this.scopeName } ); |
52 |
} |
53 |
|
54 |
addChoice( choice ) { |
55 |
this.setState( { |
56 |
visibleChoices: this.state.visibleChoices.concat( [ choice ] ), |
57 |
} ); |
58 |
} |
59 |
|
60 |
addRuleName( choice ) { |
61 |
this.setState( { |
62 |
visibleKinds: this.state.visibleKinds.concat( [ choice ] ), |
63 |
} ); |
64 |
} |
65 |
|
66 |
render() { |
67 |
const { props, state } = this; |
68 |
let choices = this._allChoices; |
69 |
let availableChoices = _( choices ) |
70 |
.filter( ( [ choice ] ) => !state.visibleChoices.includes( choice ) ) |
71 |
.sortBy( 1 ) |
72 |
.value(); |
73 |
let choiceDescriptions = _.fromPairs( choices ); |
74 |
let visibleChoices = _.sortBy( state.visibleChoices, choice => choice ? choiceDescriptions[choice] : "" ); |
75 |
|
76 |
let allKinds = KINDS_BY_SCOPE[this.scopeName]; |
77 |
let allKindNames = allKinds.map( rule => rule.name ); |
78 |
let kindGroups = _( allKindNames ) |
79 |
.difference( state.visibleKinds ) |
80 |
.map( rule_name => [ rule_name, RULE_KINDS[rule_name].description ] ) |
81 |
.sortBy( 1 ) |
82 |
.groupBy( ( [ rule_name ] ) => RULE_KINDS[rule_name].group ) |
83 |
.value(); |
84 |
|
85 |
let kindChoices = _( props.kindGroup ? { [props.kindGroup]: kindGroups[props.kindGroup] } : kindGroups ) |
86 |
.toPairs() |
87 |
.sortBy( 0 ) |
88 |
.value(); |
89 |
|
90 |
if ( kindChoices.length && kindChoices[0][1] == undefined ) kindChoices = null; |
91 |
|
92 |
let visibleKinds = state.visibleKinds.filter( rule_name => !props.kindGroup || RULE_KINDS[rule_name].group == props.kindGroup ); |
93 |
|
94 |
return <table className="table"> |
95 |
{ props.caption && <caption>{props.caption}</caption> } |
96 |
<thead> |
97 |
<tr> |
98 |
<th> </th> |
99 |
{ !visibleKinds.length ? <AllHiddenHeader /> : visibleKinds.map( rule_name => |
100 |
<th key={rule_name}>{ RULE_KINDS[rule_name].description }</th> |
101 |
) } |
102 |
{ _.isEmpty( kindChoices ) || <th className="add-rule"> |
103 |
<GroupedDropDown |
104 |
caption={__("Choose rule...")} |
105 |
value="" |
106 |
choices={ kindChoices } |
107 |
onChange={ choice => this.addRuleName(choice) } |
108 |
/> |
109 |
</th> } |
110 |
</tr> |
111 |
</thead> |
112 |
<tbody> |
113 |
{ visibleChoices.map( choice => <tr key={choice}> |
114 |
<th scope="row" className={ choice == "" ? "default" : "" }>{ choice ? choiceDescriptions[choice] : __("Defaults") }</th> |
115 |
{ visibleKinds.map( rule_name => <td key={rule_name}> |
116 |
<RuleCell |
117 |
key={rule_name} |
118 |
ruleProperties={{ |
119 |
branchcode: this.branchcode, |
120 |
categorycode: props.category ? props.category.categorycode : undefined, |
121 |
[this.ruleKey]: choice, |
122 |
rule_name |
123 |
}} |
124 |
onChangeRule={props.onChangeRule} |
125 |
onFocusRule={props.onFocusRule} |
126 |
/> |
127 |
</td> ) } |
128 |
</tr> ) } |
129 |
{ _.isEmpty( availableChoices ) || <tr> |
130 |
<th scope="row"> |
131 |
<DropDown |
132 |
caption={this._dropDownCaption} |
133 |
value="" |
134 |
choices={availableChoices} |
135 |
onChange={ choice => this.addChoice(choice) } |
136 |
/> |
137 |
</th> |
138 |
</tr> } |
139 |
</tbody> |
140 |
</table>; |
141 |
} |
142 |
} |
143 |
|
144 |
export class CategoryRuleEditor extends RuleEditor { |
145 |
get scopeName() { return "branchcode_categorycode"; } |
146 |
get ruleKey() { return "categorycode"; } |
147 |
get _dropDownCaption() { return __("Choose category..."); } |
148 |
|
149 |
get _allChoices() { |
150 |
return Object.keys( Koha.PATRON_CATEGORIES ).map( categorycode => [ categorycode, Koha.PATRON_CATEGORIES[categorycode].description ] ); |
151 |
} |
152 |
|
153 |
get _configuredChoices() { |
154 |
return _.uniq( this._ownRules.map( rule => rule.categorycode ) ); |
155 |
} |
156 |
} |
157 |
|
158 |
// This has to be slightly differently set up than the CategoryRuleEditor, because it's used for |
159 |
// both branch/itemtype and branch/category/itemtype rules. |
160 |
export class ItemTypeRuleEditor extends RuleEditor { |
161 |
static get defaultProps() { |
162 |
return { scopeName: "branchcode_itemtype" }; |
163 |
} |
164 |
|
165 |
get scopeName() { return this.props.scopeName; } |
166 |
get ruleKey() { return "itemtype"; } |
167 |
get _dropDownCaption() { return __("Choose item type..."); } |
168 |
|
169 |
get _allChoices() { |
170 |
return Object.keys( Koha.ITEM_TYPES ).map( itemtype => [ itemtype, Koha.ITEM_TYPES[itemtype].translated_description ] ); |
171 |
} |
172 |
|
173 |
get _configuredChoices() { |
174 |
return _.uniq( this._ownRules.map( rule => rule.itemtype ) ); |
175 |
} |
176 |
} |
177 |
|
178 |
export class CategoryItemTypeRuleEditor extends React.Component { |
179 |
get branchname() { |
180 |
return this.props.branch ? this.props.branch.branchname : null; |
181 |
} |
182 |
|
183 |
render() { |
184 |
const props = this.props; |
185 |
var defaults = props.category == null; |
186 |
|
187 |
return <section className={ "category-rules" + ( defaults ? " category-default-rules" : "" ) }> |
188 |
<h4>{ defaults ? __("Defaults for all categories") : props.category.description }</h4> |
189 |
<div className="table-scroll-wrapper"> |
190 |
<ItemTypeRuleEditor |
191 |
branch={props.branch} |
192 |
kindGroup={props.kindGroup} |
193 |
category={props.category} |
194 |
scopeName="branchcode_categorycode_itemtype" |
195 |
onChangeRule={props.onChangeRule} |
196 |
onFocusRule={props.onFocusRule} |
197 |
/> |
198 |
</div> |
199 |
</section>; |
200 |
} |
201 |
} |
202 |
|
203 |
function BranchGlobalRuleEditor( { branch, kindGroup, onChangeRule, onFocusRule } ) { |
204 |
let allKinds = KINDS_BY_SCOPE["branchcode"]; |
205 |
let allKindNames = allKinds.filter( rule => !kindGroup || RULE_KINDS[ rule.name ].group == kindGroup ).map( rule => rule.name ); |
206 |
|
207 |
return <table className="table"> |
208 |
<thead> |
209 |
<tr> |
210 |
{ !allKindNames.length ? <AllHiddenHeader /> : allKindNames.map( rule_name => |
211 |
<th key={rule_name}>{ RULE_KINDS[rule_name].description }</th> |
212 |
) } |
213 |
</tr> |
214 |
</thead> |
215 |
<tbody> |
216 |
<tr> |
217 |
{ allKindNames.map( rule_name => <td key={rule_name}> |
218 |
<RuleCell |
219 |
key={rule_name} |
220 |
ruleProperties={{ |
221 |
branchcode: branch ? branch.branchcode : "", |
222 |
rule_name |
223 |
}} |
224 |
onChangeRule={onChangeRule} |
225 |
onFocusRule={onFocusRule} |
226 |
/> |
227 |
</td> ) } |
228 |
</tr> |
229 |
</tbody> |
230 |
</table>; |
231 |
} |
232 |
|
233 |
export class BranchRuleEditor extends React.Component { |
234 |
constructor( props ) { |
235 |
super( props ); |
236 |
this.state = { |
237 |
visibleCategories: _.union( [ "" ], _.uniq( rules.getAllDefined( { branchcode: this.branchcode, scope_name: "branchcode_categorycode_itemtype" } ) ).map( rule => rule.categorycode ) ), |
238 |
}; |
239 |
} |
240 |
|
241 |
get branchcode() { |
242 |
return this.props.branch ? this.props.branch.branchcode : ""; |
243 |
} |
244 |
|
245 |
addCategory( choice ) { |
246 |
this.setState( { |
247 |
visibleCategories: this.state.visibleCategories.concat( [ choice ] ), |
248 |
} ); |
249 |
} |
250 |
|
251 |
render() { |
252 |
const { props, state } = this; |
253 |
var defaults = props.branch == null; |
254 |
|
255 |
let allCategories = Object.keys( Koha.PATRON_CATEGORIES ).map( categorycode => [ categorycode, Koha.PATRON_CATEGORIES[categorycode].description ] ); |
256 |
let availableCategories = _( allCategories ) |
257 |
.filter( ( [ categorycode ] ) => !state.visibleCategories.includes( categorycode ) ) |
258 |
.sortBy( 1 ) |
259 |
.value(); |
260 |
let categoryDescriptions = _.fromPairs( allCategories ); |
261 |
let visibleCategories = _.sortBy( state.visibleCategories, category => category ? categoryDescriptions[category] : "" ); |
262 |
|
263 |
return <section className={ "branch-rules" + ( defaults ? " default-rules" : "" ) }> |
264 |
<h2>{ defaults ? __( "Defaults for all libraries" ) : props.branch.branchname }</h2> |
265 |
<h3>{__( "Rules only by library" )}</h3> |
266 |
<BranchGlobalRuleEditor |
267 |
branch={props.branch} |
268 |
kindGroup={props.kindGroup} |
269 |
onChangeRule={props.onChangeRule} |
270 |
onFocusRule={props.onFocusRule} |
271 |
/> |
272 |
<CategoryRuleEditor |
273 |
caption={__( "Rules by category" )} |
274 |
branch={props.branch} |
275 |
kindGroup={props.kindGroup} |
276 |
onChangeRule={props.onChangeRule} |
277 |
onFocusRule={props.onFocusRule} |
278 |
/> |
279 |
<ItemTypeRuleEditor |
280 |
caption={__( "Rules by item type" )} |
281 |
branch={props.branch} |
282 |
kindGroup={props.kindGroup} |
283 |
onChangeRule={props.onChangeRule} |
284 |
onFocusRule={props.onFocusRule} |
285 |
/> |
286 |
<h3>{__( "Rules by category and item type" )}</h3> |
287 |
{ visibleCategories.map( categorycode => |
288 |
<CategoryItemTypeRuleEditor |
289 |
key={categorycode} |
290 |
branch={props.branch} |
291 |
kindGroup={props.kindGroup} |
292 |
category={categorycode ? Koha.PATRON_CATEGORIES[categorycode] : null} |
293 |
onChangeRule={props.onChangeRule} |
294 |
onFocusRule={props.onFocusRule} |
295 |
/> |
296 |
) } |
297 |
{_.isEmpty( availableCategories ) || |
298 |
<h4> |
299 |
<DropDown |
300 |
caption={__("Define item-type rules for category...")} |
301 |
value="" |
302 |
choices={ availableCategories } |
303 |
onChange={ choice => this.addCategory(choice) } |
304 |
/> |
305 |
</h4> } |
306 |
</section>; |
307 |
} |
308 |
} |