Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Phyks
kresus
Commits
0112f21f
Commit
0112f21f
authored
Mar 24, 2018
by
Phyks (Lucas Verney)
Browse files
Use fuzzy selectors for category and types everywhere
Fix issue #601
parent
78b38e05
Changes
6
Hide whitespace changes
Inline
Side-by-side
client/components/operations/category-select.js
View file @
0112f21f
...
...
@@ -3,44 +3,53 @@ import PropTypes from 'prop-types';
import
{
createSelector
}
from
'
reselect
'
;
import
{
connect
}
from
'
react-redux
'
;
import
Select
from
'
react-select
'
;
import
{
NONE_CATEGORY_ID
}
from
'
../../helpers
'
;
import
{
get
}
from
'
../../store
'
;
class
CategorySelect
extends
React
.
Component
{
handleChange
=
event
=>
this
.
props
.
onChange
(
event
.
target
.
value
);
handleChange
=
selectedValue
=>
{
let
value
=
NONE_CATEGORY_ID
;
if
(
selectedValue
)
{
value
=
selectedValue
.
value
;
}
if
(
value
!==
this
.
props
.
selectedValue
)
{
return
this
.
props
.
onChange
(
value
);
}
};
render
()
{
le
t
style
=
this
.
props
.
borderColor
cons
t
style
=
this
.
props
.
borderColor
?
{
borderRight
:
`5px solid
${
this
.
props
.
borderColor
}
`
}
:
null
;
return
(
<
select
className
=
"
form-control btn-transparent
"
<
Select
value
=
{
this
.
props
.
selectedValue
}
style
=
{
style
}
id
=
{
this
.
props
.
id
}
onChange
=
{
this
.
handleChange
}
>
{
this
.
props
.
categorie
s
}
<
/select
>
onChange
=
{
this
.
handleChange
}
options
=
{
this
.
props
.
option
s
}
/
>
);
}
}
function
categoryToOption
(
cat
)
{
return
(
<
option
key
=
{
cat
.
id
}
value
=
{
cat
.
id
}
>
{
cat
.
title
}
<
/option
>
);
}
const
options
=
createSelector
(
state
=>
get
.
categories
(
state
),
cats
=>
{
// Put "No category" on top of the list.
let
ops
=
[
categoryToOption
(
cats
.
find
(
cat
=>
cat
.
id
===
NONE_CATEGORY_ID
))];
return
ops
.
concat
(
cats
.
filter
(
cat
=>
cat
.
id
!==
NONE_CATEGORY_ID
).
map
(
cat
=>
categoryToOption
(
cat
))
let
noneCategory
=
cats
.
find
(
cat
=>
cat
.
id
===
NONE_CATEGORY_ID
);
return
[
{
value
:
noneCategory
.
id
,
label
:
noneCategory
.
title
}
].
concat
(
cats
.
filter
(
cat
=>
cat
.
id
!==
NONE_CATEGORY_ID
)
.
map
(
cat
=>
({
value
:
cat
.
id
,
label
:
cat
.
title
}))
);
}
);
...
...
@@ -51,7 +60,7 @@ const Export = connect((state, props) => {
?
null
:
get
.
categoryById
(
state
,
props
.
selectedValue
).
color
;
return
{
categorie
s
:
options
(
state
),
option
s
:
options
(
state
),
borderColor
};
})(
CategorySelect
);
...
...
client/components/operations/details.js
View file @
0112f21f
...
...
@@ -43,11 +43,11 @@ let fillShowDetails = (props, askDeleteConfirm) => {
<
label
className
=
"
col-xs-4 control-label
"
>
{
$t
(
'
client.operations.amount
'
)}
<
/label
>
<
label
className
=
"
col-xs-8
"
>
{
props
.
formatCurrency
(
op
.
amount
)}
<
/label
>
<
/div
>
<
div
className
=
"
form-group clearfix
"
>
<
div
className
=
"
form-group clearfix
has-overflow
"
>
<
label
className
=
"
col-xs-4 control-label
"
>
{
$t
(
'
client.operations.type
'
)}
<
/label
>
<
div
className
=
"
col-xs-8
"
>
{
typeSelect
}
<
/div
>
<
/div
>
<
div
className
=
"
form-group clearfix
"
>
<
div
className
=
"
form-group clearfix
has-overflow
"
>
<
label
className
=
"
col-xs-4 control-label
"
>
{
$t
(
'
client.operations.category
'
)}
<
/label
>
<
div
className
=
"
col-xs-8
"
>
{
categorySelect
}
<
/div
>
<
/div
>
...
...
client/components/operations/search.js
View file @
0112f21f
import
React
from
'
react
'
;
import
PropTypes
from
'
prop-types
'
;
import
Select
from
'
react-select
'
;
import
moment
from
'
moment
'
;
...
...
@@ -12,52 +12,92 @@ import AmountInput from '../ui/amount-input';
import
DatePicker
from
'
../ui/date-picker
'
;
import
FoldablePanel
from
'
../ui/foldable-panel
'
;
const
SearchCategorySelect
=
connect
(
const
ANY_TYPE_ID
=
''
;
const
SearchTypeSelect
=
connect
(
state
=>
{
return
{
defaultValue
:
get
.
searchFields
(
state
).
categoryId
,
categories
:
get
.
categori
es
(
state
)
defaultValue
:
get
.
searchFields
(
state
).
type
,
types
:
get
.
typ
es
(
state
)
};
},
dispatch
=>
{
return
{
handleChange
(
event
)
{
actions
.
setSearchField
(
dispatch
,
'
categoryId
'
,
event
.
target
.
value
);
handleOperationType
:
selectedValue
=>
{
let
value
=
ANY_TYPE_ID
;
if
(
selectedValue
)
{
value
=
selectedValue
.
value
;
}
actions
.
setSearchField
(
dispatch
,
'
type
'
,
value
);
}
};
}
)(
props
=>
{
let
{
defaultValue
,
categories
,
handleChange
}
=
props
;
let
unknownType
=
props
.
types
.
find
(
type
=>
type
.
name
===
UNKNOWN_OPERATION_TYPE
)
;
let
noneCategory
=
categories
.
find
(
cat
=>
cat
.
id
===
NONE_CATEGORY_ID
);
categories
=
categories
.
filter
(
cat
=>
cat
.
id
!==
NONE_CATEGORY_ID
);
// Types are not sorted.
let
types
=
[
unknownType
].
concat
(
props
.
types
.
filter
(
type
=>
type
.
name
!==
UNKNOWN_OPERATION_TYPE
)
);
let
options
=
[
<
option
key
=
"
_
"
value
=
""
>
{
$t
(
'
client.search.any_category
'
)}
<
/option>
,
<
option
key
=
{
noneCategory
.
id
}
value
=
{
noneCategory
.
id
}
>
{
noneCategory
.
title
}
<
/option
>
let
typeOptions
=
[
{
value
:
ANY_TYPE_ID
,
label
:
$t
(
'
client.search.any_type
'
)
}
].
concat
(
categories
.
map
(
cat
=>
(
<
option
key
=
{
cat
.
id
}
value
=
{
cat
.
id
}
>
{
cat
.
title
}
<
/option
>
))
types
.
map
(
type
=>
({
value
:
type
.
name
,
label
:
$t
(
`client.
${
type
.
name
}
`
)
}))
);
return
(
<
select
className
=
"
form-control
"
id
=
{
props
.
id
}
value
=
{
defaultValue
}
onChange
=
{
handleChange
}
>
{
options
}
<
/select
>
<
Select
onChange
=
{
props
.
handleOperationType
}
options
=
{
typeOptions
}
value
=
{
props
.
defaultValue
}
/
>
);
});
SearchCategorySelect
.
propTypes
=
{
// A string to link the input to a label for exemple.
id
:
PropTypes
.
string
};
const
ANY_CATEGORY_ID
=
''
;
const
SearchCategorySelect
=
connect
(
state
=>
{
return
{
defaultValue
:
get
.
searchFields
(
state
).
categoryId
,
categories
:
get
.
categories
(
state
)
};
},
dispatch
=>
{
return
{
handleChange
(
selectedValue
)
{
let
value
=
ANY_CATEGORY_ID
;
if
(
selectedValue
)
{
value
=
selectedValue
.
value
;
}
actions
.
setSearchField
(
dispatch
,
'
categoryId
'
,
value
);
}
};
}
)(
props
=>
{
let
noneCategory
=
props
.
categories
.
find
(
cat
=>
cat
.
id
===
NONE_CATEGORY_ID
);
let
categories
=
props
.
categories
.
filter
(
cat
=>
cat
.
id
!==
NONE_CATEGORY_ID
);
let
options
=
[
{
value
:
ANY_CATEGORY_ID
,
label
:
$t
(
'
client.search.any_category
'
)
},
{
value
:
noneCategory
.
id
,
label
:
noneCategory
.
title
}
].
concat
(
categories
.
map
(
cat
=>
({
value
:
cat
.
id
,
label
:
cat
.
title
})));
return
<
Select
value
=
{
props
.
defaultValue
}
onChange
=
{
props
.
handleChange
}
options
=
{
options
}
/>
;
});
const
MinDatePicker
=
connect
(
(
state
,
props
)
=>
{
...
...
@@ -130,31 +170,9 @@ class SearchComponent extends React.Component {
}
render
()
{
let
unknownType
=
this
.
props
.
types
.
find
(
type
=>
type
.
name
===
UNKNOWN_OPERATION_TYPE
);
// Types are not sorted.
let
types
=
[
unknownType
].
concat
(
this
.
props
.
types
.
filter
(
type
=>
type
.
name
!==
UNKNOWN_OPERATION_TYPE
)
);
let
typeOptions
=
[
<
option
key
=
"
_
"
value
=
""
>
{
$t
(
'
client.search.any_type
'
)}
<
/option
>
].
concat
(
types
.
map
(
type
=>
(
<
option
key
=
{
type
.
name
}
value
=
{
type
.
name
}
>
{
$t
(
`client.
${
type
.
name
}
`
)}
<
/option
>
))
);
let
handleKeyword
=
event
=>
{
this
.
props
.
setKeywords
(
event
.
target
.
value
);
};
let
handleOperationType
=
event
=>
{
this
.
props
.
setType
(
event
.
target
.
value
);
};
let
handleAmountLow
=
value
=>
{
this
.
props
.
setAmountLow
(
Number
.
isNaN
(
value
)
?
null
:
value
);
};
...
...
@@ -206,12 +224,7 @@ class SearchComponent extends React.Component {
<
label
htmlFor
=
"
type-selector
"
>
{
$t
(
'
client.search.type
'
)}
<
/label
>
<
select
className
=
"
form-control
"
id
=
"
type-selector
"
onChange
=
{
handleOperationType
}
>
{
typeOptions
}
<
/select
>
<
SearchTypeSelect
id
=
"
type-selector
"
/>
<
/div
>
<
div
className
=
"
search-amounts
"
>
...
...
@@ -267,7 +280,6 @@ class SearchComponent extends React.Component {
const
Export
=
connect
(
state
=>
{
return
{
types
:
get
.
types
(
state
),
displaySearchDetails
:
get
.
displaySearchDetails
(
state
)
};
},
...
...
@@ -283,10 +295,6 @@ const Export = connect(
actions
.
setSearchField
(
dispatch
,
'
keywords
'
,
keywords
);
},
setType
(
type
)
{
actions
.
setSearchField
(
dispatch
,
'
type
'
,
type
);
},
setAmountLow
(
amountLow
)
{
actions
.
setSearchField
(
dispatch
,
'
amountLow
'
,
amountLow
);
},
...
...
client/components/operations/type-select.js
View file @
0112f21f
...
...
@@ -3,21 +3,31 @@ import PropTypes from 'prop-types';
import
{
createSelector
}
from
'
reselect
'
;
import
{
connect
}
from
'
react-redux
'
;
import
Select
from
'
react-select
'
;
import
{
translate
as
$t
}
from
'
../../helpers
'
;
import
{
get
}
from
'
../../store
'
;
class
TypeSelect
extends
React
.
Component
{
handleChange
=
event
=>
this
.
props
.
onChange
(
event
.
target
.
value
);
handleChange
=
selectedValue
=>
{
if
(
selectedValue
&&
selectedValue
.
value
&&
selectedValue
.
value
!==
this
.
props
.
selectedValue
)
{
this
.
props
.
onChange
(
selectedValue
.
value
);
}
};
render
()
{
return
(
<
select
className
=
"
form-control btn-transparent
"
<
Select
value
=
{
this
.
props
.
selectedValue
}
id
=
{
this
.
props
.
id
}
onChange
=
{
this
.
handleChange
}
>
{
this
.
props
.
types
}
<
/select
>
clearable
=
{
false
}
onChange
=
{
this
.
handleChange
}
options
=
{
this
.
props
.
types
}
/
>
);
}
}
...
...
@@ -25,11 +35,10 @@ class TypeSelect extends React.Component {
const
options
=
createSelector
(
state
=>
get
.
types
(
state
),
types
=>
{
return
types
.
map
(
type
=>
(
<
option
key
=
{
`operation-type-select-operation-
${
type
.
id
}
`
}
value
=
{
type
.
name
}
>
{
$t
(
`client.
${
type
.
name
}
`
)}
<
/option
>
));
return
types
.
map
(
type
=>
({
value
:
type
.
name
,
label
:
$t
(
`client.
${
type
.
name
}
`
)
}));
}
);
...
...
client/components/settings/bank-accesses/add-operation-modal.js
View file @
0112f21f
...
...
@@ -123,7 +123,7 @@ class AddOperationModal extends React.Component {
/
>
<
/div
>
<
div
className
=
"
form-group
"
>
<
div
className
=
"
form-group
has-overflow
"
>
<
label
className
=
"
control-label
"
htmlFor
=
{
`type
${
this
.
props
.
account
.
id
}
`
}
>
{
$t
(
'
client.addoperationmodal.type
'
)}
<
/label
>
...
...
@@ -158,7 +158,7 @@ class AddOperationModal extends React.Component {
/
>
<
/div
>
<
div
className
=
"
form-group
"
>
<
div
className
=
"
form-group
has-overflow
"
>
<
label
className
=
"
control-label
"
htmlFor
=
{
`category
${
this
.
props
.
account
.
id
}
`
}
>
...
...
client/themes/default/style.css
View file @
0112f21f
...
...
@@ -704,6 +704,12 @@ form.search p.search-buttons button:last-child {
}
}
.operation-panel
td
>
.Select
,
.modal-body
.Select
{
overflow
:
visible
;
z-index
:
auto
;
}
.input-group-addon
{
color
:
#a2a2a2
;
}
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment