Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
GGCE
GGCE Web
Commits
48a51a3c
Commit
48a51a3c
authored
May 05, 2022
by
Maksym Tishchenko
Browse files
Link Overviews to Accession/Inventory
parent
7d069c61
Changes
15
Hide whitespace changes
Inline
Side-by-side
workspaces/client/locales/en/common.json
View file @
48a51a3c
...
...
@@ -78,6 +78,7 @@
"fromDate"
:
"From date"
,
"toDate"
:
"To date"
,
"toDateExclusive"
:
"To date (exclusive)"
,
"browse"
:
"Browse {{what, lowercase}}"
,
"true"
:
"True"
,
"tick"
:
"Tick"
,
"untick"
:
"Untick"
,
...
...
workspaces/client/src/model/gringlobal/AccessionFilter.ts
View file @
48a51a3c
...
...
@@ -5,8 +5,10 @@ import StringFilter from '@gringlobal-ce/client/model/common/StringFilter';
import
NumberFilter
from
'
@gringlobal-ce/client/model/common/NumberFilter
'
;
import
SiteFilter
from
'
@gringlobal-ce/client/model/gringlobal/SiteFilter
'
;
import
TaxonomySpeciesFilter
from
'
@gringlobal-ce/client/model/gringlobal/TaxonomySpeciesFilter
'
;
import
InventoryFilter
from
'
./InventoryFilter
'
;
import
AccessionSourceFilter
from
'
./AccessionSourceFilter
'
;
import
InventoryFilter
from
'
@gringlobal-ce/client/model/gringlobal/InventoryFilter
'
;
import
AccessionSourceFilter
from
'
@gringlobal-ce/client/model/gringlobal/AccessionSourceFilter
'
;
import
AccessionActionFilter
from
"
@gringlobal-ce/client/model/gringlobal/AccessionActionFilter
"
;
import
AccessionIprFilter
from
"
@gringlobal-ce/client/model/gringlobal/AccessionIprFilter
"
;
/**
* AccessionFilter
...
...
@@ -45,6 +47,8 @@ class AccessionFilter {
public
taxonomySpecies
?:
TaxonomySpeciesFilter
;
public
inventories
?:
InventoryFilter
;
public
sources
?:
AccessionSourceFilter
;
public
accessionActions
?:
AccessionActionFilter
;
public
accessionIprs
?:
AccessionIprFilter
;
public
_text
?:
string
;
}
...
...
workspaces/client/src/model/gringlobal/AccessionInvNameFilter.ts
0 → 100644
View file @
48a51a3c
import
DateFilter
from
'
@gringlobal-ce/client/model/gringlobal/DateFilter
'
;
import
InventoryFilter
from
'
@gringlobal-ce/client/model/gringlobal/InventoryFilter
'
;
import
NameGroupFilter
from
'
@gringlobal-ce/client/model/gringlobal/NameGroupFilter
'
;
import
StringFilter
from
"
@gringlobal-ce/client/model/common/StringFilter
"
;
/**
* AccessionInvNameFilter
*
* GRIN-Global CE API
*/
class
AccessionInvNameFilter
{
public
NOT
:
AccessionInvNameFilter
;
public
NULL
:
string
[];
public
NOTNULL
:
string
[];
public
AND
:
AccessionInvNameFilter
;
public
OR
:
AccessionInvNameFilter
;
public
id
:
number
[];
public
createdBy
:
number
[];
public
createdDate
:
DateFilter
;
public
modifiedBy
:
number
[];
public
modifiedDate
:
DateFilter
;
public
ownedBy
:
number
[];
public
ownedDate
:
DateFilter
;
public
inventory
:
InventoryFilter
;
public
categoryCode
:
string
[];
public
plantName
:
StringFilter
;
public
plantNameRank
:
number
;
public
webVisible
:
boolean
;
public
nameGroup
:
NameGroupFilter
;
}
export
default
AccessionInvNameFilter
;
workspaces/client/src/model/gringlobal/AccessionIprFilter.ts
0 → 100644
View file @
48a51a3c
import
AccessionFilter
from
'
@gringlobal-ce/client/model/gringlobal/AccessionFilter
'
;
import
DateFilter
from
'
@gringlobal-ce/client/model/gringlobal/DateFilter
'
;
import
StringFilter
from
"
@gringlobal-ce/client/model/common/StringFilter
"
;
/**
* AccessionIprFilter
*
* GRIN-Global CE API
*/
class
AccessionIprFilter
{
public
NOT
:
AccessionIprFilter
;
public
NULL
:
string
[];
public
NOTNULL
:
string
[];
public
AND
:
AccessionIprFilter
;
public
OR
:
AccessionIprFilter
;
public
id
:
number
[];
public
createdBy
:
number
[];
public
createdDate
:
DateFilter
;
public
modifiedBy
:
number
[];
public
modifiedDate
:
DateFilter
;
public
ownedBy
:
number
[];
public
ownedDate
:
DateFilter
;
public
acceptedDate
:
DateFilter
;
public
accession
:
AccessionFilter
;
public
expectedDate
:
DateFilter
;
public
expiredDate
:
DateFilter
;
public
iprCropName
:
StringFilter
;
public
iprFullName
:
StringFilter
;
public
iprNumber
:
StringFilter
;
public
issuedDate
:
DateFilter
;
public
typeCode
:
string
[];
}
export
default
AccessionIprFilter
;
workspaces/client/src/model/gringlobal/InventoryFilter.ts
View file @
48a51a3c
...
...
@@ -4,6 +4,8 @@ import DateFilter from '@gringlobal-ce/client/model/gringlobal/DateFilter';
import
NumberFilter
from
'
@gringlobal-ce/client/model/common/NumberFilter
'
;
import
StringFilter
from
'
@gringlobal-ce/client/model/common/StringFilter
'
;
import
SiteFilter
from
'
@gringlobal-ce/client/model/gringlobal/SiteFilter
'
;
import
InventoryActionFilter
from
"
@gringlobal-ce/client/model/gringlobal/InventoryActionFilter
"
;
import
AccessionInvNameFilter
from
"
@gringlobal-ce/client/model/gringlobal/AccessionInvNameFilter
"
;
/**
* InventoryFilter
...
...
@@ -60,6 +62,9 @@ class InventoryFilter {
public
storageLocationPart3
?:
StringFilter
;
public
storageLocationPart4
?:
StringFilter
;
public
webAvailabilityNote
?:
StringFilter
;
public
accessionInvGroup
?:
number
[];
public
actions
?:
InventoryActionFilter
;
public
names
?:
AccessionInvNameFilter
;
public
_text
?:
string
;
public
barcode
?:
string
[];
...
...
workspaces/ui-express/locales/en/express.json
View file @
48a51a3c
...
...
@@ -173,6 +173,20 @@
}
},
"public"
:
{
"prettyF"
:
{
"NOT"
:
"Excluding {{what}}"
,
"le"
:
"{{what}} ≤"
,
"ge"
:
"{{what}} ≥"
,
"lt"
:
"{{what}} <"
,
"gt"
:
"{{what}} >"
,
"less"
:
"Less filters..."
,
"more"
:
"More filters..."
,
"_text"
:
"Full-text"
,
"dataExists"
:
"Data exists"
,
"dataNotProvided"
:
"Data not provided"
,
"applyingFilters"
:
"Applying filters..."
,
"includeSystem"
:
"Include system"
},
"c"
:
{
"printLabel"
:
{
"noTemplateSelected"
:
"Please select a template"
,
...
...
workspaces/ui-express/package.json
View file @
48a51a3c
...
...
@@ -128,6 +128,7 @@
"eslint-webpack-plugin"
:
"^2.0.0"
,
"fetch-mock"
:
"^9.0.0"
,
"file-loader"
:
"^6.0.0"
,
"flattenjs"
:
"^2.0.0"
,
"git-revision-webpack-plugin"
:
"^3.0.0"
,
"html-webpack-plugin"
:
"^4.0.0"
,
"html-webpack-skip-assets-plugin"
:
"^0.0.2"
,
...
...
workspaces/ui-express/src/accession/constants.ts
View file @
48a51a3c
...
...
@@ -3,7 +3,8 @@ export const SAGA_LIST_ACCESSIONS = 'saga/accession/public/LIST';
export
const
SAGA_LIST_ACCESSIONS_MCPD
=
'
saga/accession/public/LISTMCPD
'
;
export
const
SAGA_RECEIVE_ACCESSION_INVENTORIES
=
'
saga/accession/public/RECEIVE_INVENTORIES
'
;
export
const
SAGA_UPLOAD_ACCESSION_ATTACHMENTS
=
'
saga/accession/public/UPLOAD_ACCESSION_ATTACHMENTS
'
;
export
const
SAGA_RECEIVE_ACCESSION_SUCCESS
=
'
saga/INVENTORY_GROUP/public/RECEIVE_INVENTORY_SUCCESS
'
;
export
const
SAGA_RECEIVE_ACCESSION_SUCCESS
=
'
saga/accession/public/RECEIVE_ACCESSION_SUCCESS
'
;
export
const
SAGA_RECEIVE_ACCESSION_LIST_SUCCESS
=
'
saga/accession/public/RECEIVE_ACCESSION_LIST_SUCCESS
'
;
export
const
RECEIVE_ACCESSION
=
'
success/accession/public/RECEIVE_ACCESSION
'
;
export
const
RECEIVE_ACCESSIONS_LIST
=
'
success/accession/public/LIST
'
;
...
...
workspaces/ui-express/src/accession/ui/SummaryPage.tsx
View file @
48a51a3c
...
...
@@ -3,12 +3,13 @@ import { connect } from 'react-redux';
import
{
compose
}
from
'
redux
'
;
import
{
withTranslation
,
WithTranslation
}
from
'
react-i18next
'
;
import
memoize
from
'
memoize-one
'
;
import
{
set
,
get
,
isEmpty
,
merge
}
from
'
lodash
'
;
// Actions
import
navigateTo
from
'
@gringlobal-ce/client/action/navigation
'
;
// Layout
import
{
createStyles
,
Paper
,
Card
,
CardContent
}
from
'
@material-ui/core
'
;
import
{
Button
,
Card
,
CardContent
,
createStyles
,
IconButton
,
Paper
,
WithWidth
,
withWidth
}
from
'
@material-ui/core
'
;
import
SlotLayout
from
'
@gringlobal-ce/client/ui/common/layout/SlotLayout
'
;
import
{
withStyles
,
WithStyles
}
from
'
@material-ui/core/styles
'
;
...
...
@@ -27,23 +28,42 @@ import { Properties, PropertiesItem } from '@gringlobal-ce/client/ui/common/Prop
import
Number
from
'
@gringlobal-ce/client/ui/common/Number
'
;
import
FiltersButton
from
'
accession/ui/c/FiltersButton
'
;
import
Filters
from
'
accession/ui/c/Filters
'
;
import
{
Geography
}
from
"
@gringlobal-ce/client/model/gringlobal
"
;
import
{
YesNoToBoolean
}
from
"
@gringlobal-ce/client/utilities
"
;
import
FileCopyOutlinedIcon
from
'
@material-ui/icons/FileCopyOutlined
'
;
import
PrettyFilters
from
"
ui/common/filter/PrettyFilters
"
;
import
ListIcon
from
'
@material-ui/icons/List
'
;
import
{
Breakpoint
}
from
"
@material-ui/core/styles/createBreakpoints
"
;
import
{
listAccessionsAction
}
from
"
accession/action/public
"
;
const
styles
=
()
=>
createStyles
({
groupByContainer
:
{
'
display
'
:
'
flex
'
as
const
,
'
flexDirection
'
:
'
row
'
as
const
,
'
justifyContent
'
:
'
flex-start
'
as
const
,
display
:
'
flex
'
as
const
,
flexDirection
:
'
row
'
as
const
,
justifyContent
:
'
flex-start
'
as
const
,
height
:
'
5rem
'
,
gap
:
'
4px
'
,
whiteSpace
:
'
nowrap
'
,
'
@media only screen and (max-height: 500px)
'
:
{
flexDirection
:
'
column
'
as
const
,
flexWrap
:
'
wrap
'
as
const
,
},
},
groupBy
:
{
padding
:
'
0.5em 1em
'
,
margin
:
'
0.5em 0
'
,
padding
:
'
1em 1em 0
'
,
cursor
:
'
pointer
'
as
const
,
width
:
'
160px
'
,
fontSize
:
'
0.8em
'
,
height
:
'
100%
'
,
boxSizing
:
'
border-box
'
,
'
&:nth-child(even)
'
:
{
backgroundColor
:
'
#f8f7f5
'
,
},
'
&:nth-child(odd)
'
:
{
backgroundColor
:
'
#f3f2ee
'
,
},
},
groupBySelected
:
{
borderBottom
:
'
4px solid #e240a3
'
,
},
blowUp
:
{
height
:
'
100%
'
,
...
...
@@ -58,36 +78,53 @@ const styles = () => createStyles({
flexShrink
:
0
,
},
},
actionsWrapper
:
{
position
:
'
absolute
'
,
right
:
0
},
listIcon
:
{
marginBottom
:
'
2px
'
}
});
const
ORIGINONLY
=
{
sources
:
{
origin
:
true
}
};
const
WITHOUTSYSTEM
=
{
inventories
:
{
includeSystem
:
false
}
};
class
SummaryPage
extends
React
.
Component
<
PropsFromRedux
&
WithTranslation
&
WithStyles
>
{
const
FILTER_TYPES
=
{
stringArray
:
'
STRING_ARRAY
'
,
// string[]
stringFilter
:
'
STRING_FILTER
'
,
// StringFilter = { eq, contains, sw }
boolean
:
'
BOOLEAN
'
,
codeValue
:
'
CODE_VALUE
'
}
const
mobile
=
[
'
xs
'
,
'
sm
'
]
as
Breakpoint
[];
class
SummaryPage
extends
React
.
Component
<
PropsFromRedux
&
WithTranslation
&
WithStyles
&
WithWidth
>
{
private
wrapperRef
=
null
;
private
static
SUMMARIES
=
[
{
groupBy
:
'
site.siteShortName
'
,
title
:
[
'
client:model.Accession.site
'
,
'
client:model._.site
'
]
},
{
groupBy
:
'
accessionNumberPart1
'
,
title
:
'
client:model.Accession.accessionNumberPart1
'
},
{
groupBy
:
'
statusCode
'
,
title
:
'
client:model.Accession.statusCode
'
,
codeGroup
:
Accession
.
CodeGroup
.
statusCode
},
{
groupBy
:
'
mlsStatus
'
,
title
:
'
client:model.Accession.mlsStatus
'
,
codeGroup
:
Accession
.
CodeGroup
.
mlsStatus
},
{
groupBy
:
'
improvementStatusCode
'
,
title
:
'
client:model.Accession.improvementStatusCode
'
,
codeGroup
:
Accession
.
CodeGroup
.
improvementStatusCode
},
{
groupBy
:
'
lifeFormCode
'
,
title
:
'
client:model.Accession.lifeFormCode
'
},
{
groupBy
:
'
reproductiveUniformityCode
'
,
title
:
'
client:model.Accession.reproductiveUniformityCode
'
},
{
groupBy
:
'
taxonomySpecies.taxonomyGenus.genusName
'
,
title
:
'
client:model.TaxonomyGenus.genusName
'
},
{
groupBy
:
'
taxonomySpecies.name
'
,
title
:
'
client:model.Accession.taxonomySpecies
'
},
{
groupBy
:
'
accessionSources.sourceTypeCode
'
,
title
:
'
accession.public.p.summary.originSourceTypeCode
'
,
codeGroup
:
AccessionSource
.
CodeGroup
.
sourceTypeCode
,
filter
:
{
...
ORIGINONLY
}
},
{
groupBy
:
'
accessionSources.acquisitionSource
'
,
title
:
'
client:model.AccessionSource.acquisitionSource
'
,
codeGroup
:
AccessionSource
.
CodeGroup
.
acquisitionSource
,
filter
:
{
...
ORIGINONLY
}
},
{
groupBy
:
'
accessionSources.geography.countryCode
'
,
title
:
'
client:model.Geography.countryCode
'
,
codeGroup
:
'
GEOGRAPHY_COUNTRY_CODE
'
,
filter
:
{
...
ORIGINONLY
}
},
{
groupBy
:
'
accessionActions.actionNameCode
'
,
title
:
'
client:model.AccessionAction.actionNameCode
'
,
codeGroup
:
AccessionAction
.
CodeGroup
.
actionNameCode
},
{
groupBy
:
'
accessionIprs.typeCode
'
,
title
:
'
client:model.AccessionIpr.typeCode
'
,
codeGroup
:
AccessionIpr
.
CodeGroup
.
typeCode
},
{
groupBy
:
'
inventories.formTypeCode
'
,
title
:
'
client:model.Inventory.formTypeCode
'
,
codeGroup
:
Inventory
.
CodeGroup
.
formTypeCode
,
filter
:
{
...
WITHOUTSYSTEM
}
},
{
groupBy
:
'
inventories.inventoryMaintenancePolicy.maintenanceName
'
,
title
:
'
client:model.Inventory.inventoryMaintenancePolicy
'
,
filter
:
{
...
WITHOUTSYSTEM
}
},
{
groupBy
:
'
inventories.availabilityStatusCode
'
,
title
:
'
client:model.Inventory.availabilityStatusCode
'
,
codeGroup
:
Inventory
.
CodeGroup
.
availabilityStatusCode
,
filter
:
{
...
WITHOUTSYSTEM
}
},
{
groupBy
:
'
inventories.isAvailable
'
,
title
:
'
client:model.Inventory.isAvailable
'
,
filter
:
{
...
WITHOUTSYSTEM
}
},
{
groupBy
:
'
inventories.storageLocationPart1
'
,
title
:
'
client:model.Inventory.storageLocationPart1
'
,
filter
:
{
...
WITHOUTSYSTEM
}
},
{
groupBy
:
'
inventories.names.categoryCode
'
,
title
:
'
client:model.AccessionInvName.categoryCode
'
,
codeGroup
:
AccessionInvName
.
CodeGroup
.
categoryCode
},
{
groupBy
:
'
inventories.names.nameGroup.groupName
'
,
title
:
'
client:model.AccessionInvGroup.groupName
'
},
{
groupBy
:
'
inventories.actions.actionNameCode
'
,
title
:
'
client:model.InventoryAction.actionNameCode
'
,
codeGroup
:
InventoryAction
.
CodeGroup
.
actionNameCode
},
{
groupBy
:
'
site.siteShortName
'
,
title
:
[
'
client:model.Accession.site
'
,
'
client:model._.site
'
]
,
filterType
:
FILTER_TYPES
.
stringFilter
},
{
groupBy
:
'
accessionNumberPart1
'
,
title
:
'
client:model.Accession.accessionNumberPart1
'
,
filterType
:
FILTER_TYPES
.
stringFilter
},
{
groupBy
:
'
statusCode
'
,
title
:
'
client:model.Accession.statusCode
'
,
codeGroup
:
Accession
.
CodeGroup
.
statusCode
,
filterType
:
FILTER_TYPES
.
codeValue
},
{
groupBy
:
'
mlsStatus
'
,
title
:
'
client:model.Accession.mlsStatus
'
,
codeGroup
:
Accession
.
CodeGroup
.
mlsStatus
,
filterType
:
FILTER_TYPES
.
codeValue
},
{
groupBy
:
'
improvementStatusCode
'
,
title
:
'
client:model.Accession.improvementStatusCode
'
,
codeGroup
:
Accession
.
CodeGroup
.
improvementStatusCode
,
filterType
:
FILTER_TYPES
.
codeValue
},
{
groupBy
:
'
lifeFormCode
'
,
title
:
'
client:model.Accession.lifeFormCode
'
,
codeGroup
:
Accession
.
CodeGroup
.
lifeFormCode
,
filterType
:
FILTER_TYPES
.
codeValue
},
{
groupBy
:
'
reproductiveUniformityCode
'
,
title
:
'
client:model.Accession.reproductiveUniformityCode
'
,
codeGroup
:
Accession
.
CodeGroup
.
reproductiveUniformityCode
,
filterType
:
FILTER_TYPES
.
codeValue
},
{
groupBy
:
'
taxonomySpecies.taxonomyGenus.genusName
'
,
title
:
'
client:model.TaxonomyGenus.genusName
'
,
filterType
:
FILTER_TYPES
.
stringFilter
},
{
groupBy
:
'
taxonomySpecies.name
'
,
title
:
'
client:model.Accession.taxonomySpecies
'
,
filterType
:
FILTER_TYPES
.
stringFilter
},
{
groupBy
:
'
accessionSources.sourceTypeCode
'
,
title
:
'
accession.public.p.summary.originSourceTypeCode
'
,
codeGroup
:
AccessionSource
.
CodeGroup
.
sourceTypeCode
,
filter
Path
:
'
sources.sourceTypeCode
'
,
filter
:
{
...
ORIGINONLY
},
filterType
:
FILTER_TYPES
.
codeValue
},
{
groupBy
:
'
accessionSources.acquisitionSource
'
,
title
:
'
client:model.AccessionSource.acquisitionSource
'
,
codeGroup
:
AccessionSource
.
CodeGroup
.
acquisitionSource
,
filter
Path
:
'
sources.acquisitionSource
'
,
filter
:
{
...
ORIGINONLY
},
filterType
:
FILTER_TYPES
.
codeValue
},
{
groupBy
:
'
accessionSources.geography.countryCode
'
,
title
:
'
client:model.Geography.countryCode
'
,
codeGroup
:
Geography
.
CodeGroup
.
countryCode
,
filterPath
:
'
sources.geography.countryCode
'
,
filter
:
{
...
ORIGINONLY
},
filterType
:
FILTER_TYPES
.
codeValue
},
{
groupBy
:
'
accessionActions.actionNameCode
'
,
title
:
'
client:model.AccessionAction.actionNameCode
'
,
codeGroup
:
AccessionAction
.
CodeGroup
.
actionNameCode
,
filterType
:
FILTER_TYPES
.
codeValue
},
{
groupBy
:
'
accessionIprs.typeCode
'
,
title
:
'
client:model.AccessionIpr.typeCode
'
,
codeGroup
:
AccessionIpr
.
CodeGroup
.
typeCode
,
filterType
:
FILTER_TYPES
.
codeValue
},
{
groupBy
:
'
inventories.formTypeCode
'
,
title
:
'
client:model.Inventory.formTypeCode
'
,
codeGroup
:
Inventory
.
CodeGroup
.
formTypeCode
,
filter
:
{
...
WITHOUTSYSTEM
}
,
filterType
:
FILTER_TYPES
.
codeValue
},
{
groupBy
:
'
inventories.inventoryMaintenancePolicy.maintenanceName
'
,
title
:
'
client:model.Inventory.inventoryMaintenancePolicy
'
,
filter
:
{
...
WITHOUTSYSTEM
}
,
filterType
:
FILTER_TYPES
.
stringFilter
},
{
groupBy
:
'
inventories.availabilityStatusCode
'
,
title
:
'
client:model.Inventory.availabilityStatusCode
'
,
codeGroup
:
Inventory
.
CodeGroup
.
availabilityStatusCode
,
filter
:
{
...
WITHOUTSYSTEM
}
,
filterType
:
FILTER_TYPES
.
codeValue
},
{
groupBy
:
'
inventories.isAvailable
'
,
title
:
'
client:model.Inventory.isAvailable
'
,
filter
:
{
...
WITHOUTSYSTEM
}
,
filterType
:
FILTER_TYPES
.
boolean
,
filterPath
:
'
inventories.available
'
},
{
groupBy
:
'
inventories.storageLocationPart1
'
,
title
:
'
client:model.Inventory.storageLocationPart1
'
,
filter
:
{
...
WITHOUTSYSTEM
}
,
filterType
:
FILTER_TYPES
.
stringFilter
},
{
groupBy
:
'
inventories.names.categoryCode
'
,
title
:
'
client:model.AccessionInvName.categoryCode
'
,
codeGroup
:
AccessionInvName
.
CodeGroup
.
categoryCode
,
filterType
:
FILTER_TYPES
.
codeValue
},
{
groupBy
:
'
inventories.names.nameGroup.groupName
'
,
title
:
'
client:model.AccessionInvGroup.groupName
'
,
filterType
:
FILTER_TYPES
.
stringFilter
},
{
groupBy
:
'
inventories.actions.actionNameCode
'
,
title
:
'
client:model.InventoryAction.actionNameCode
'
,
codeGroup
:
InventoryAction
.
CodeGroup
.
actionNameCode
,
filterType
:
FILTER_TYPES
.
codeValue
},
];
public
state
=
{
...
...
@@ -98,6 +135,7 @@ class SummaryPage extends React.Component<PropsFromRedux & WithTranslation & Wit
public
constructor
(
props
)
{
super
(
props
);
this
.
wrapperRef
=
React
.
createRef
();
}
public
static
getDerivedStateFromProps
(
props
,
state
)
{
...
...
@@ -133,6 +171,15 @@ class SummaryPage extends React.Component<PropsFromRedux & WithTranslation & Wit
// }
}
private
onCopy
=
()
=>
{
const
rawText
=
this
.
wrapperRef
.
current
.
innerText
;
const
formattedText
=
rawText
.
split
(
'
\n
'
).
reduce
((
acc
,
el
,
i
)
=>
{
return
acc
+
el
+
(
i
%
2
?
'
\n
'
:
'
\t
'
);
// --> first cell \t second cell \n
},
''
);
const
formattedTextWithLink
=
formattedText
+
`\n
${
window
.
location
}
`
;
(
navigator
as
any
).
clipboard
.
writeText
(
formattedTextWithLink
)
};
private
decodeCodeValue
=
(
groupName
:
string
,
value
:
string
):
string
=>
{
if
(
value
===
'
NULL
'
)
{
// API uses this key
return
value
;
...
...
@@ -152,7 +199,7 @@ class SummaryPage extends React.Component<PropsFromRedux & WithTranslation & Wit
const
summary
=
SummaryPage
.
SUMMARIES
.
filter
((
summary
)
=>
summary
.
groupBy
===
group
)[
0
];
// Get overview statistics
const
customizedFilter
=
{
...
accessionFilter
,
...
summary
.
filter
}
;
const
customizedFilter
=
merge
({},
summary
.
filter
,
accessionFilter
)
;
console
.
log
(
`Getting overview for
${
group
}
`
,
summary
,
customizedFilter
);
AccessionService
.
getOverview
(
group
,
customizedFilter
).
then
((
overview
)
=>
{
...
...
@@ -201,13 +248,61 @@ class SummaryPage extends React.Component<PropsFromRedux & WithTranslation & Wit
console
.
log
(
`Selected group
${
group
}
`
);
this
.
loadNumbers
(
group
);
}
private
filterByNumber
=
(
name
,
group
)
=>
{
const
{
accessionFilter
}
=
this
.
state
;
const
summary
=
SummaryPage
.
SUMMARIES
.
filter
((
summary
)
=>
summary
.
groupBy
===
group
)[
0
]
const
filter
=
{}
const
filterPath
=
summary
.
filterPath
??
group
const
arrayOfKeys
=
filterPath
.
split
(
'
.
'
)
const
lastPathKey
=
arrayOfKeys
.
pop
();
const
withoutLastKey
=
arrayOfKeys
.
join
(
'
.
'
);
if
(
name
===
"
NULL
"
)
{
set
(
filter
,
`
${
withoutLastKey
?
`
${
withoutLastKey
}
.`
:
''
}
NULL`
,
[...(
get
(
accessionFilter
,
`
${
filterPath
}
.NULL`
)
??
[]),
lastPathKey
])
this
.
setState
({
accessionFilter
:
merge
({},
accessionFilter
,
filter
)
})
this
.
loadNumbers
(
group
);
return
}
switch
(
summary
.
filterType
)
{
case
FILTER_TYPES
.
stringFilter
:
{
set
(
filter
,
`
${
filterPath
}
.eq`
,
[
name
])
break
;
}
case
FILTER_TYPES
.
stringArray
:
{
set
(
filter
,
filterPath
,
[
name
])
break
;
}
case
FILTER_TYPES
.
boolean
:
{
set
(
filter
,
filterPath
,
YesNoToBoolean
(
name
))
break
;
}
case
FILTER_TYPES
.
codeValue
:
{
set
(
filter
,
filterPath
,
name
)
break
;
}
}
this
.
setState
({
accessionFilter
:
merge
({},
accessionFilter
,
filter
)
})
this
.
loadNumbers
(
group
);
}
private
getSortedValues
=
memoize
((
values
:
any
[])
=>
[
...
values
].
sort
((
a
,
b
)
=>
b
.
value
-
a
.
value
));
p
ublic
render
()
{
const
{
t
,
classes
}
=
this
.
props
;
p
rivate
browseList
=
()
=>
{
const
{
navigateTo
,
listAccessionsAction
}
=
this
.
props
;
const
{
accessionFilter
}
=
this
.
state
;
console
.
log
(
'
Rendering summary. This is important!
'
);
listAccessionsAction
(
accessionFilter
)
navigateTo
(
'
/a
'
)
}
public
render
()
{
const
{
t
,
width
,
classes
}
=
this
.
props
;
const
{
accessionFilter
,
group
}
=
this
.
state
;
const
isMobile
=
mobile
.
indexOf
(
width
)
!==
-
1
;
console
.
log
(
'
Rendering summary. This is important!
'
,
accessionFilter
);
return
(
<>
...
...
@@ -223,27 +318,44 @@ class SummaryPage extends React.Component<PropsFromRedux & WithTranslation & Wit
<
SlotLayout
fixedContent
=
{
<>
<
Paper
square
style
=
{
{
flexGrow
:
1
,
overflow
:
'
scroll
'
}
}
>
<
Paper
square
style
=
{
{
flexGrow
:
1
,
overflow
X
:
'
auto
'
,
overflowY
:
'
hidden
'
}
}
>
<
div
className
=
{
classes
.
groupByContainer
}
>
{
SummaryPage
.
SUMMARIES
.
map
((
summary
)
=>
(
<
div
key
=
{
summary
.
groupBy
}
className
=
{
classes
.
groupBy
}
onClick
=
{
this
.
selectGroup
(
summary
.
groupBy
,
t
(
summary
.
title
))
}
>
{
t
(
summary
.
title
)
}
</
div
>
<
div
key
=
{
summary
.
groupBy
}
className
=
{
`
${
classes
.
groupBy
}
${
summary
.
groupBy
===
group
?
classes
.
groupBySelected
:
''
}
`
}
onClick
=
{
this
.
selectGroup
(
summary
.
groupBy
,
t
(
summary
.
title
))
}
>
{
t
(
summary
.
title
)
}
</
div
>
))
}
</
div
>
</
Paper
>
{
!
isEmpty
(
accessionFilter
)
&&
<
PrettyFilters
prefix
=
"Accession"
filterObj
=
{
accessionFilter
}
onSubmit
=
{
(
filter
)
=>
this
.
setState
({
accessionFilter
:
filter
})
}
labels
=
{
SummaryPage
.
SUMMARIES
.
map
((
summary
)
=>
[
summary
.
filterPath
??
summary
.
groupBy
,
{
title
:
summary
.
title
,
codeGroup
:
summary
.
codeGroup
}
])
}
/>
}
</>
}
>
<
div
className
=
{
classes
.
mainWrapper
}
>
<
div
className
=
{
classes
.
diagramWrapper
}
>
<
TreeMap
className
=
{
classes
.
blowUp
}
data
=
{
this
.
state
.
data
}
/>
<
div
className
=
{
classes
.
actionsWrapper
}
>
<
Button
type
=
"button"
onClick
=
{
this
.
browseList
}
>
{
!
isMobile
&&
<
span
className
=
"mr-5"
>
{
`
${
t
(
'
common:label.browse
'
,
{
what
:
t
(
'
client:model.name.Accession_plural
'
)
})
}
`
}
</
span
>
}
<
ListIcon
className
=
{
classes
.
listIcon
}
/>
</
Button
>
<
IconButton
type
=
"button"
onClick
=
{
this
.
onCopy
}
title
=
{
t
(
'
common:action.copyToClipboard
'
)
}
>
<
FileCopyOutlinedIcon
/>
</
IconButton
>
</
div
>
<
TreeMap
onClick
=
{
(
name
)
=>
this
.
filterByNumber
(
name
,
group
)
}
className
=
{
classes
.
blowUp
}
data
=
{
this
.
state
.
data
}
/>
</
div
>
{
this
.
state
.
data
&&
this
.
state
.
data
.
children
&&
(
<
Card
>
<
CardContent
>
<
CardContent
ref
=
{
this
.
wrapperRef
}
>
<
Properties
>
{
this
.
getSortedValues
(
this
.
state
.
data
.
children
).
map
((
item
)
=>
<
PropertiesItem
key
=
{
`
${
item
.
name
}
-
${
item
.
value
}
`
}
title
=
{
item
.
missingData
?
<
i
>
{
item
.
label
||
item
.
name
}
</
i
>
:
item
.
label
||
item
.
name
}
numeric
>
<
Number
value
=
{
item
.
value
}
/>
<
a
onClick
=
{
()
=>
this
.
filterByNumber
(
item
.
name
,
group
)
}
>
<
Number
value
=
{
item
.
value
}
/>
</
a
>
</
PropertiesItem
>
)
}
</
Properties
>
...
...
@@ -265,6 +377,7 @@ const mapStateToProps = (state) => ({
const
mapDispatch
=
{
navigateTo
,
listAccessionsAction
,
}
type
PropsFromRedux
=
ReturnType
<
typeof
mapStateToProps
>
&
typeof
mapDispatch
;
...
...
@@ -273,4 +386,5 @@ export default compose(
connect
(
mapStateToProps
,
mapDispatch
),
withStyles
(
styles
),
withTranslation
(),
withWidth
(),
)(
SummaryPage
);
workspaces/ui-express/src/d3js/TreeMap.tsx
View file @
48a51a3c
...
...
@@ -7,12 +7,13 @@ interface IProps {
className
:
string
;
data
:
any
;
colors
?:
any
;
onClick
?:
(
name
:
string
)
=>
void
;
}
/**
* TreeMap based on https://observablehq.com/@d3/zoomable-treemap
*/
export
const
TreeMap
=
({
nullLabel
=
'
NULL
'
,
className
,
data
,
colors
}:
IProps
)
=>
{
export
const
TreeMap
=
({
nullLabel
=
'
NULL
'
,
className
,
data
,
colors
,
onClick
}:
IProps
)
=>
{
const
chartRef
=
React
.
useRef
(
null
);
// useEffect
...
...
@@ -87,7 +88,9 @@ export const TreeMap = ({ nullLabel = 'NULL', className, data, colors }: IProps)
node
.
append
(
'
rect
'
)
.
attr
(
'
id
'
,
(
d
)
=>
(
d
.
leafUid
=
DOM
.
uid
(
'
leaf
'
)).
id
)
.
attr
(
'
fill
'
,
(
d
)
=>
d
===
root
?
'
#fff
'
:
d
.
children
?
'
#ccc
'
:
color
(
d
.
data
.
name
))
.
attr
(
'
stroke
'
,
'
#fff
'
);
.
attr
(
'
stroke
'
,
'
#fff
'
)
.
attr
(
'
cursor
'
,
onClick
?
(
d
)
=>
d
!==
root
?
'
pointer
'
:
null
:
null
)
.
on
(
'
click
'
,
onClick
?
(
d
)
=>
d
!==
root
?
onClick
(
d
.
data
.
name
)
:
null
:
null
);
node
.
append
(
'
clipPath
'
)
.
attr
(
'
id
'
,
(
d
)
=>
(
d
.
clipUid
=
DOM
.
uid
(
'
clip
'
)).
id
)
...
...
workspaces/ui-express/src/inventory/ui/SummaryPage.tsx
View file @
48a51a3c
...
...
@@ -3,12 +3,13 @@ import { connect } from 'react-redux';
import
{
compose
}
from
'
redux
'
;
import
{
withTranslation
,
WithTranslation
}
from
'
react-i18next
'
;
import
memoize
from
'
memoize-one
'
;
import
{
set
,
isEmpty
,
merge
,
get
}
from
'
lodash
'
;
// Actions
import
navigateTo
from
'
@gringlobal-ce/client/action/navigation
'
;
// Layout
import
{
Card
,
CardContent
,
createStyles
,
Paper
}
from
'
@material-ui/core
'
;
import
{
Button
,
Card
,
CardContent
,
createStyles
,
IconButton
,
Paper
,
WithWidth
,
withWidth
}
from
'
@material-ui/core
'
;
import
SlotLayout
from
'
@gringlobal-ce/client/ui/common/layout/SlotLayout
'
;
import
{
withStyles
,
WithStyles
}
from
'
@material-ui/core/styles
'
;
...
...
@@ -27,23 +28,41 @@ import { Properties, PropertiesItem } from '@gringlobal-ce/client/ui/common/Prop
import
Number
from
'
@gringlobal-ce/client/ui/common/Number
'
;
import
FiltersButton
from
'
accession/ui/c/FiltersButton
'
;
import
Filters
from
'
inventory/ui/c/Filters
'
;
import
{
YesNoToBoolean
}
from
"
@gringlobal-ce/client/utilities
"
;
import
FileCopyOutlinedIcon
from
'
@material-ui/icons/FileCopyOutlined
'
;
import
PrettyFilters
from
"
ui/common/filter/PrettyFilters
"
;
import
ListIcon
from
'
@material-ui/icons/List
'
;
import
{
Breakpoint
}
from
"
@material-ui/core/styles/createBreakpoints
"
;
import
{
listInventoryAction
}
from
"
inventory/action/public
"
;
const
styles
=
()
=>
createStyles
({
groupByContainer
:
{
'
display
'
:
'
flex
'
as
const
,
'
flexDirection
'
:
'
row
'
as
const
,
'
justifyContent
'
:
'
flex-start
'
as
const
,
display
:
'
flex
'
as
const
,
flexDirection
:
'
row
'
as
const
,
justifyContent
:
'
flex-start
'
as
const
,
height
:
'
5rem
'
,
gap
:
'
4px
'
,
whiteSpace
:
'
nowrap
'
,
'
@media only screen and (max-height: 500px)
'
:
{
flexDirection
:
'
column
'
as
const
,
flexWrap
:
'
wrap
'
as
const
,
},
},
groupBy
:
{
padding
:
'
0.5em 1em
'
,
margin
:
'
0.5em 0
'
,
padding
:
'
1em 1em 0
'
,
cursor
:
'
pointer
'
as
const
,
width
:
'
160px
'
,
fontSize
:
'
0.8em
'
,
height
:
'
100%
'
,
boxSizing
:
'
border-box
'
,
'
&:nth-child(even)
'
:
{
backgroundColor
:
'
#f8f7f5
'
,
},
'
&:nth-child(odd)
'
:
{
backgroundColor
:
'
#f3f2ee
'
,
},
},
groupBySelected
:
{
borderBottom
:
'
4px solid #e240a3
'
,
},
blowUp
:
{
height
:
'
100%
'
,
...
...
@@ -58,31 +77,48 @@ const styles = () => createStyles({
flexShrink
:
0
,
},