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
ICAT
Datahub
Commits
a46d8f22
Commit
a46d8f22
authored
Jul 26, 2021
by
Alejandro De Maria Antolinos
Browse files
Merge branch 'issue_507-investigations_pagination' into 'master'
Issue 507 investigations pagination See merge request
!539
parents
b67a7f29
e13d0e68
Pipeline
#51322
passed with stages
in 4 minutes and 22 seconds
Changes
8
Pipelines
2
Hide whitespace changes
Inline
Side-by-side
src/components/Investigation/InvestigationDateFilter.js
View file @
a46d8f22
...
...
@@ -21,9 +21,14 @@ const getHrefByInstrument = (instrumentName) => {
function
InvestigationDateFilter
(
props
)
{
const
{
rootClassName
,
value
,
onDayChange
,
onClear
,
startValue
,
onStartDayChange
,
onStartClear
,
startPlaceHolder
,
endValue
,
onEndDayChange
,
onEndClear
,
endPlaceHolder
,
instrumentName
,
showStatisticsMenu
,
withJupyter
,
...
...
@@ -33,19 +38,40 @@ function InvestigationDateFilter(props) {
<
div
className
=
{
rootClassName
}
>
<
DayPickerInput
inputProps
=
{{
className
:
styles
.
dateFilterInput
}}
value
=
{
v
alue
}
placeholder
=
"
Filter by date
"
value
=
{
startV
alue
}
placeholder
=
{
startPlaceHolder
?
startPlaceHolder
:
'
Filter by date
'
}
format
=
{
INVESTIGATION_DATE_FORMAT
}
formatDate
=
{
formatDate
}
parseDate
=
{
parseDate
}
onDayChange
=
{
onDayChange
}
onDayChange
=
{
on
Start
DayChange
}
dayPickerProps
=
{{
todayButton
:
'
Today
'
,
}}
/
>
<
Button
disabled
=
{
!
value
}
onClick
=
{
onClear
}
disabled
=
{
!
startValue
}
onClick
=
{
onStartClear
}
bsStyle
=
"
link
"
className
=
{
styles
.
clearButton
}
>
<
Glyphicon
glyph
=
"
remove
"
/>
<
/Button
>
<
DayPickerInput
inputProps
=
{{
className
:
styles
.
dateFilterInput
}}
value
=
{
endValue
}
placeholder
=
{
endPlaceHolder
?
endPlaceHolder
:
'
Filter by date
'
}
format
=
{
INVESTIGATION_DATE_FORMAT
}
formatDate
=
{
formatDate
}
parseDate
=
{
parseDate
}
onDayChange
=
{
onEndDayChange
}
dayPickerProps
=
{{
todayButton
:
'
Today
'
,
}}
/
>
<
Button
disabled
=
{
!
endValue
}
onClick
=
{
onEndClear
}
bsStyle
=
"
link
"
className
=
{
styles
.
clearButton
}
>
...
...
src/components/Investigation/InvestigationTable.js
View file @
a46d8f22
...
...
@@ -16,11 +16,11 @@ import {
}
from
'
./utils
'
;
import
InvestigationDateFilter
from
'
./InvestigationDateFilter
'
;
import
styles
from
'
./InvestigationTable.module.css
'
;
import
{
useHistory
,
useLocation
}
from
'
react-router
'
;
import
{
useQuery
}
from
'
../../helpers/hooks
'
;
import
{
useSelector
}
from
'
react-redux
'
;
import
{
INVESTIGATION_DATE_FORMAT
}
from
'
../../constants
'
;
import
UI
from
'
../../config/ui
'
;
import
{
useResource
}
from
'
rest-hooks
'
;
import
InvestigationResource
from
'
../../resources/investigation
'
;
function
getLgHeaderStyle
(
width
,
hidden
)
{
return
{
...
...
@@ -76,7 +76,7 @@ function getColumns({
{
text
:
'
Beamline
'
,
dataField
:
'
visitId
'
,
dataField
:
'
instrument
'
,
formatter
:
(
_
,
investigation
)
=>
(
<
span
style
=
{{
fontWeight
:
'
bold
'
}}
>
{
beamlineFormatter
(
investigation
)}
...
...
@@ -165,42 +165,93 @@ function getColumns({
function
InvestigationTable
(
props
)
{
const
{
investigations
,
withProposalLinks
=
false
,
withInvestigationStats
=
false
,
showStatisticsMenu
=
false
,
instrumentName
,
withUserPortalLink
=
false
,
withJupyter
=
false
,
filter
,
}
=
props
;
const
[
sizePerPage
,
setSizePerPage
]
=
React
.
useState
(
25
);
const
[
page
,
setPage
]
=
React
.
useState
(
1
);
const
[
search
,
setSearch
]
=
React
.
useState
(
''
);
const
[
startDate
,
setStartDate
]
=
React
.
useState
(
undefined
);
const
[
endDate
,
setEndDate
]
=
React
.
useState
(
undefined
);
const
[
sortField
,
setSortField
]
=
React
.
useState
(
'
startDate
'
);
const
[
sortOrder
,
setSortOrder
]
=
React
.
useState
(
-
1
);
let
fetchingParams
=
{
limit
:
sizePerPage
,
skip
:
sizePerPage
*
(
page
-
1
),
sortBy
:
sortField
?
sortField
:
'
startDate
'
,
sortOrder
:
sortOrder
?
sortOrder
:
-
1
,
};
if
(
filter
)
{
fetchingParams
=
{
...
fetchingParams
,
filter
};
}
if
(
startDate
)
{
fetchingParams
=
{
...
fetchingParams
,
startDate
:
moment
(
startDate
).
format
(
'
YYYY-MM-DD
'
),
};
}
if
(
endDate
)
{
fetchingParams
=
{
...
fetchingParams
,
endDate
:
moment
(
endDate
).
format
(
'
YYYY-MM-DD
'
),
};
}
if
(
instrumentName
)
{
fetchingParams
=
{
...
fetchingParams
,
instrument
:
instrumentName
};
}
if
(
search
)
{
fetchingParams
=
{
...
fetchingParams
,
search
};
}
const
data
=
useResource
(
InvestigationResource
.
listShape
(),
fetchingParams
);
const
totalSize
=
data
&&
data
.
length
>
0
?
data
[
0
].
meta
?.
page
?.
total
:
0
;
const
user
=
useSelector
((
state
)
=>
state
.
user
);
const
{
sessionId
,
isAdministrator
}
=
user
;
const
history
=
useHistory
();
const
query
=
useQuery
();
const
location
=
useLocation
();
function
handleStartDateChange
(
date
)
{
setStartDate
(
date
);
setPage
(
1
);
}
const
dateForFiltering
=
query
.
has
(
'
date
'
)
?
moment
(
query
.
get
(
'
date
'
),
moment
.
HTML5_FMT
.
DATE
)
:
undefined
;
function
handleEndDateChange
(
date
)
{
setEndDate
(
date
);
setPage
(
1
);
}
function
handleDateChange
(
date
)
{
const
newQuery
=
new
URLSearchParams
(
query
);
function
handleStartDateClear
()
{
setStartDate
(
undefined
);
setPage
(
1
);
}
if
(
date
)
{
newQuery
.
set
(
'
date
'
,
moment
(
date
).
format
(
moment
.
HTML5_FMT
.
DATE
));
}
else
{
newQuery
.
delete
(
'
date
'
);
}
function
handleEndDateClear
()
{
setEndDate
(
undefined
);
setPage
(
1
);
}
history
.
replace
(
`
${
location
.
pathname
}
?
${
newQuery
.
toString
()}
`
);
function
handleTableChange
(
page
,
sizePerPage
,
sortField
,
sortOrder
)
{
setPage
(
page
);
setSizePerPage
(
sizePerPage
);
setSortField
(
sortField
);
const
order
=
sortOrder
?
(
sortOrder
===
'
asc
'
?
1
:
-
1
)
:
undefined
;
setSortOrder
(
order
);
}
function
handleDateClear
()
{
const
newQuery
=
new
URLSearchParams
(
query
);
newQuery
.
delete
(
'
date
'
);
history
.
replace
(
`
${
location
.
pathname
}
?
${
newQuery
.
toString
()}
`
);
function
onSearch
(
query
)
{
setSearch
(
query
);
setPage
(
1
);
}
const
expandRow
=
{
...
...
@@ -235,36 +286,27 @@ function InvestigationTable(props) {
),
};
function
isInvestigationMatching
(
investigation
)
{
if
(
!
dateForFiltering
)
{
return
true
;
}
return
investigation
.
endDate
?
dateForFiltering
.
isBetween
(
investigation
.
startDate
,
investigation
.
endDate
,
'
day
'
,
'
[]
'
)
:
dateForFiltering
.
isSame
(
investigation
.
startDate
,
'
day
'
);
}
return
(
<>
<
div
className
=
{
styles
.
wrapper
}
>
<
InvestigationDateFilter
rootClassName
=
{
styles
.
filter
}
value
=
{
dateForFiltering
&&
dateForFiltering
.
toDate
()}
onDayChange
=
{
handleDateChange
}
onClear
=
{
handleDateClear
}
startValue
=
{
startDate
}
onStartDayChange
=
{
handleStartDateChange
}
onStartClear
=
{
handleStartDateClear
}
startPlaceHolder
=
{
'
Filter between dates
'
}
endValue
=
{
endDate
}
onEndDayChange
=
{
handleEndDateChange
}
onEndClear
=
{
handleEndDateClear
}
endPlaceHolder
=
{
'
Filter between dates
'
}
showStatisticsMenu
=
{
showStatisticsMenu
}
instrumentName
=
{
instrumentName
}
withJupyter
=
{
withJupyter
}
/
>
<
/div
>
<
ResponsiveTable
data
=
{
investigations
.
filter
(
isInvestigationMatching
)}
remote
=
{
true
}
data
=
{
data
}
pageOptions
=
{{
showTotal
:
true
,
sizePerPageList
:
[
...
...
@@ -272,6 +314,8 @@ function InvestigationTable(props) {
{
text
:
'
50
'
,
value
:
50
},
{
text
:
'
100
'
,
value
:
100
},
],
sizePerPage
,
totalSize
,
}}
columns
=
{
getColumns
({
showProposalLinks
:
withProposalLinks
||
isAdministrator
,
...
...
@@ -282,6 +326,9 @@ function InvestigationTable(props) {
(
UI
.
userPortal
.
isLinkEnabled
&&
isAdministrator
),
})}
expandRow
=
{
expandRow
}
handleTableChange
=
{
handleTableChange
}
onSearch
=
{
onSearch
}
delay
=
{
1250
}
/
>
<
/
>
);
...
...
src/components/Table/ResponsiveTable.js
View file @
a46d8f22
...
...
@@ -7,105 +7,140 @@ import { LinkContainer } from 'react-router-bootstrap';
import
styles
from
'
./ResponsiveTable.module.css
'
;
import
{
getCurrentBreakpoint
}
from
'
../../helpers
'
;
class
ResponsiveTable
extends
React
.
Component
{
/**
* This method will calculate the headerStyle based on the ResponsiveHeaderStyle and the size of the window
* @param {array} columns BootstrapTable2 columns
*/
configure
(
columns
)
{
const
size
=
getCurrentBreakpoint
();
/**
* This method will calculate the headerStyle based on the ResponsiveHeaderStyle and the size of the window
* @param {array} columns BootstrapTable2 columns
*/
function
configure
(
columns
)
{
const
size
=
getCurrentBreakpoint
();
return
columns
.
map
((
column
)
=>
column
.
responsiveHeaderStyle
&&
column
.
responsiveHeaderStyle
[
size
]
?
{
...
column
,
headerStyle
:
column
.
responsiveHeaderStyle
[
size
],
hidden
:
column
.
responsiveHeaderStyle
[
size
].
hidden
,
}
:
column
);
}
return
columns
.
map
((
column
)
=>
column
.
responsiveHeaderStyle
&&
column
.
responsiveHeaderStyle
[
size
]
?
{
...
column
,
headerStyle
:
column
.
responsiveHeaderStyle
[
size
],
hidden
:
column
.
responsiveHeaderStyle
[
size
].
hidden
,
}
:
column
);
}
function
renderActionButton
(
btnProps
)
{
const
{
label
,
icon
,
...
otherProps
}
=
btnProps
;
return
(
<
Button
key
=
{
label
}
{...
otherProps
}
>
{
icon
&&
<
Glyphicon
className
=
{
styles
.
actionIcon
}
glyph
=
{
icon
}
/>
}
{
label
}
<
/Button
>
);
}
export
default
function
ResponsiveTable
(
props
)
{
const
{
SearchBar
}
=
Search
;
const
{
keyField
,
data
,
columns
,
search
,
expandRow
,
actions
,
onSearch
,
defaultSorted
,
selectRow
,
pageOptions
,
rowEvents
,
rowClasses
,
delay
,
remote
=
false
,
}
=
props
;
function
onColumnMatch
({
searchText
,
value
,
column
,
row
})
{
if
(
column
.
onMatch
)
{
return
column
.
onMatch
(
searchText
,
value
,
row
,
column
.
formatExtraData
);
}
renderActionButton
(
btnProps
)
{
const
{
label
,
icon
,
...
otherProps
}
=
btnProps
;
return
(
<
Button
key
=
{
label
}
{...
otherProps
}
>
{
icon
&&
<
Glyphicon
className
=
{
styles
.
actionIcon
}
glyph
=
{
icon
}
/>
}
{
label
}
<
/Button
>
value
!==
null
&&
value
!==
undefined
&&
value
.
toString
().
toLowerCase
().
includes
(
searchText
)
);
}
render
()
{
const
{
SearchBar
}
=
Search
;
function
handleTableChange
(
type
,
{
page
,
sizePerPage
,
sortField
,
sortOrder
}
)
{
if
(
props
.
handleTableChange
)
{
props
.
handleTableChange
(
page
,
sizePerPage
,
sortField
,
sortOrder
);
}
}
function
onColumnMatch
({
searchText
,
value
,
column
,
row
})
{
if
(
column
.
onMatch
)
{
return
column
.
onMatch
(
searchText
,
value
,
row
,
column
.
formatExtraData
);
const
remoteProps
=
remote
?
{
remote
:
{
filter
:
true
,
pagination
:
true
,
sort
:
true
,
cellEdit
:
false
,
},
}
:
{};
return
(
value
!==
null
&&
value
!==
undefined
&&
value
.
toString
().
toLowerCase
().
includes
(
searchText
)
);
}
return
(
<
ToolkitProvider
keyField
=
{
this
.
props
.
keyField
||
'
id
'
}
data
=
{
this
.
props
.
data
}
columns
=
{
this
.
configure
(
this
.
props
.
columns
)}
search
=
{{
onColumnMatch
,
...
this
.
props
.
search
}}
expandRow
=
{
this
.
props
.
expandRow
}
>
{({
baseProps
,
searchProps
})
=>
(
<>
<
div
className
=
{
styles
.
bar
}
>
<
div
className
=
{
styles
.
actions
}
>
{(
this
.
props
.
actions
||
[]).
map
((
action
)
=>
{
const
{
href
,
...
btnProps
}
=
action
;
if
(
!
href
)
{
return
this
.
renderActionButton
(
btnProps
);
return
(
<
ToolkitProvider
keyField
=
{
keyField
||
'
id
'
}
data
=
{
data
}
columns
=
{
configure
(
columns
)}
search
=
{{
onColumnMatch
,
...
search
}}
expandRow
=
{
expandRow
}
>
{({
baseProps
,
searchProps
})
=>
(
<>
<
div
className
=
{
styles
.
bar
}
>
<
div
className
=
{
styles
.
actions
}
>
{(
actions
||
[]).
map
((
action
)
=>
{
const
{
href
,
...
btnProps
}
=
action
;
if
(
!
href
)
{
return
renderActionButton
(
btnProps
);
}
return
(
<
LinkContainer
key
=
{
btnProps
.
label
}
to
=
{
action
.
href
}
>
{
renderActionButton
(
btnProps
)}
<
/LinkContainer
>
);
})}
<
/div
>
<
div
className
=
{
styles
.
search
}
>
<
SearchBar
searchText
=
{
searchProps
.
searchText
}
delay
=
{
delay
?
delay
:
250
}
onSearch
=
{(
query
)
=>
{
searchProps
.
onSearch
(
query
);
if
(
onSearch
)
{
onSearch
(
query
);
}
return
(
<
LinkContainer
key
=
{
btnProps
.
label
}
to
=
{
action
.
href
}
>
{
this
.
renderActionButton
(
btnProps
)}
<
/LinkContainer
>
);
})}
<
/div
>
<
div
className
=
{
styles
.
search
}
>
<
SearchBar
searchText
=
{
searchProps
.
searchText
}
onSearch
=
{(
query
)
=>
{
if
(
this
.
props
.
onSearch
)
{
this
.
props
.
onSearch
(
query
);
}
searchProps
.
onSearch
(
query
);
}}
/
>
<
/div
>
}}
/
>
<
/div
>
<
BootstrapTable2
striped
hover
condensed
showTotal
defaultSorted
=
{
this
.
props
.
defaultSorted
}
selectRow
=
{
this
.
props
.
selectRow
}
expandRow
=
{
this
.
props
.
expandRow
}
pagination
=
{
paginationFactory
(
this
.
props
.
pageOptions
)}
rowEvents
=
{
this
.
props
.
rowEvents
}
rowClasses
=
{
this
.
props
.
rowClasses
}
noDataIndication
=
"
No data to display
"
{...
baseProps
}
/
>
<
/
>
)}
<
/ToolkitProvider
>
);
}
<
/div
>
<
BootstrapTable2
{...
remoteProps
}
pagination
=
{
paginationFactory
(
pageOptions
)}
onTableChange
=
{
handleTableChange
}
striped
hover
condensed
defaultSorted
=
{
defaultSorted
}
selectRow
=
{
selectRow
}
expandRow
=
{
expandRow
}
rowEvents
=
{
rowEvents
}
rowClasses
=
{
rowClasses
}
noDataIndication
=
"
No data to display
"
{...
baseProps
}
/
>
<
/
>
)}
<
/ToolkitProvider
>
);
}
export
default
ResponsiveTable
;
src/containers/BeamlineData/BeamlineDataTable.js
View file @
a46d8f22
import
React
from
'
react
'
;
import
InvestigationTable
from
'
../../components/Investigation/InvestigationTable
'
;
import
{
useResource
}
from
'
rest-hooks
'
;
import
InvestigationResource
from
'
../../resources/investigation
'
;
import
{
useParams
}
from
'
react-router
'
;
import
UI
from
'
../../config/ui
'
;
function
BeamlineDataTable
(
props
)
{
const
{
name
}
=
useParams
();
const
{
showStatisticsMenu
=
false
}
=
props
;
const
investigations
=
useResource
(
InvestigationResource
.
listShape
(),
{
instrument
:
name
,
});
return
(
<
InvestigationTable
investigations
=
{
investigations
}
withInvestigationStats
withProposalLinks
showStatisticsMenu
=
{
showStatisticsMenu
}
...
...
src/containers/ClosedData/EmbargoedInvestigationsTable.js
View file @
a46d8f22
import
React
from
'
react
'
;
import
{
useResource
}
from
'
rest-hooks
'
;
import
InvestigationTable
from
'
../../components/Investigation/InvestigationTable
'
;
import
InvestigationResource
from
'
../../resources/investigation
'
;
function
EmbargoedInvestigationsTable
()
{
const
investigations
=
useResource
(
InvestigationResource
.
listShape
(),
{
filter
:
'
embargoed
'
,
});
return
<
InvestigationTable
investigations
=
{
investigations
}
/>
;
return
<
InvestigationTable
filter
=
"
embargoed
"
/>
;
}
export
default
EmbargoedInvestigationsTable
;
src/containers/MyData/MyIndustryProposalsPanel.js
View file @
a46d8f22
import
React
from
'
react
'
;
import
{
Panel
,
Glyphicon
}
from
'
react-bootstrap
'
;
import
InvestigationTable
from
'
../../components/Investigation/InvestigationTable
'
;
import
{
useResource
}
from
'
rest-hooks
'
;
import
InvestigationResource
from
'
../../resources/investigation
'
;
const
INDUSTRY_PROPOSAL_REGEX
=
/^
(
IX|FX|IN|IM
)
/
;
function
MyIndustryProposalsPanel
()
{
const
myInvestigations
=
useResource
(
InvestigationResource
.
listShape
(),
{
filter
:
'
participant
'
,
});
// TODO replace `inv.visitId` with `inv.type !== "PROPOSAL"` when available
const
industryProposals
=
myInvestigations
.
filter
((
inv
)
=>
{
return
inv
.
visitId
===
'
PROPOSAL
'
&&
INDUSTRY_PROPOSAL_REGEX
.
test
(
inv
.
name
);
});
if
(
industryProposals
.
length
===
0
)
{
return
null
;
}
return
(
<
Panel
bsStyle
=
"
info
"
>
<
Panel
.
Heading
>
...
...
@@ -34,7 +17,7 @@ function MyIndustryProposalsPanel() {
been
scheduled
for
them
.
<
/p
>
<
InvestigationTable
investigations
=
{
industryProposals
}
filter
=
"
industry
"
withInvestigationStats
withProposalLinks
/>
...
...
src/containers/MyData/MyInvestigationsTable.js
View file @
a46d8f22
import
React
from
'
react
'
;
import
{
useResource
}
from
'
rest-hooks
'
;
import
InvestigationResource
from
'
../../resources/investigation
'
;
import
InvestigationTable
from
'
../../components/Investigation/InvestigationTable
'
;
import
UI
from
'
../../config/ui
'
;
function
MyInvestigationsTable
()
{
const
myInvestigations
=
useResource
(
InvestigationResource
.
listShape
(),
{
filter
:
'
participant
'
,
});
// TODO replace `inv.visitId` with `inv.type !== "PROPOSAL"` when available
const
mySessions
=
myInvestigations
.
filter
((
inv
)
=>
{
return
inv
.
visitId
!==
'
PROPOSAL
'
;
});
return
(
<
InvestigationTable
investigations
=
{
mySessions
}
filter
=
"
participant
"
withInvestigationStats