diff --git a/src/client/components/Main.js b/src/client/components/Main.js index 37075fec5ec4773f9e5616b3217eb5a57bb31ea9..bcac662f9182764975d4313f58dbe34c83fbdb02 100644 --- a/src/client/components/Main.js +++ b/src/client/components/Main.js @@ -51,6 +51,7 @@ let Main = props => { const { classes } = props; const ManuscriptLink = props => <NavLink to="/manuscripts" {...props}/>; +//const WorkLink = props => <NavLink to="/works" {...props}/>; return ( <div className={classes.root}> @@ -97,7 +98,7 @@ let Main = props => { <CardMedia className={classes.media} image={thumbImage} - title="Manuscripts" + title="Works" /> <CardContent className={classes.cardContent}> <Typography gutterBottom variant="h5" component="h2"> diff --git a/src/client/components/Manuscripts.js b/src/client/components/Manuscripts.js index 0773377b189066b81d14bbd599a572ccaeb43b3b..372d735d281ca41809ccdda31ab655f6007f9d0c 100644 --- a/src/client/components/Manuscripts.js +++ b/src/client/components/Manuscripts.js @@ -5,20 +5,21 @@ import ViewTabs from './ViewTabs'; import ResultTable from './ResultTable'; import LeafletMap from './LeafletMap'; import Deck from './Deck'; -import Pie from './Pie'; let Manuscripts = props => { return ( <React.Fragment> + <ViewTabs routeProps={props.routeProps} /> <Route exact path='/manuscripts' render={() => <Redirect to='manuscripts/table' />} /> - <ViewTabs routeProps={props.routeProps} /> <Route path={'/manuscripts/table'} render={routeProps => <ResultTable + resultClass='manuscripts' + columns={props.search.resultTableColumns} search={props.search} facetFilters={props.facetFilters} fetchResults={props.fetchResults} @@ -50,15 +51,6 @@ let Manuscripts = props => { data={props.search.places} />} /> - <Route - path={'/manuscripts/statistics'} - render={() => - <Pie - fetchPlaces={props.fetchPlaces} - fetchingPlaces={props.search.fetchingPlaces} - data={props.search.places} - />} - /> </React.Fragment> ); }; diff --git a/src/client/components/ResultTable.js b/src/client/components/ResultTable.js index 83845355e7aa198262116c334a8ffccc46253071..7864cd01326e76785b28589a7a4edcfb1ca68183 100644 --- a/src/client/components/ResultTable.js +++ b/src/client/components/ResultTable.js @@ -3,44 +3,23 @@ import PropTypes from 'prop-types'; import { withStyles } from '@material-ui/core/styles'; import Table from '@material-ui/core/Table'; import TableBody from '@material-ui/core/TableBody'; -import TableCell from '@material-ui/core/TableCell'; +import ResultTableCell from './ResultTableCell'; import TableRow from '@material-ui/core/TableRow'; import Typography from '@material-ui/core/Typography'; import CircularProgress from '@material-ui/core/CircularProgress'; import purple from '@material-ui/core/colors/purple'; import ResultTableHead from './ResultTableHead'; -import { orderBy, has } from 'lodash'; import { parse } from 'query-string'; const styles = () => ({ tableContainer: { - //marginTop: 72, overflow: 'auto', width: '100%', height: 'calc(100% - 72px)' }, - table: { - //marginTop: 72, - //minWidth: 700, - //overflowX: 'auto', - //backgroundColor: theme.palette.background.paper - }, paginationRow: { borderBottom: '1px solid lightgrey' }, - valueList: { - paddingLeft: 15 - }, - valueListNoBullets: { - listStyle: 'none', - paddingLeft: 0 - }, - withFilter: { - //minWidth: 170 - }, - wideColumn: { - minWidth: 170 - }, infoIcon: { paddingTop: 15 }, @@ -66,7 +45,7 @@ class ResultTable extends React.Component { if (this.props.routeProps.location.search === '') { page = this.props.search.page === -1 ? 0 : this.props.search.page; this.props.routeProps.history.push({ - pathname: '/manuscripts/table', + pathname: `/${this.props.resultClass}/table`, search: `?page=${this.props.search.page}`, }); //console.log(`result table mounted WITHOUT page parameter, set page to ${page}`); @@ -80,26 +59,26 @@ class ResultTable extends React.Component { componentDidUpdate = prevProps => { if (prevProps.search.page != this.props.search.page) { - this.props.fetchResults('manuscripts'); + this.props.fetchResults(this.props.resultClass); this.props.routeProps.history.push({ - pathname: '/manuscripts/table', + pathname: `/${this.props.resultClass}/table`, search: `?page=${this.props.search.page}`, }); } if (prevProps.facetFilters != this.props.facetFilters) { this.props.updatePage(0); if (this.props.search.page == 0) { - this.props.fetchResults('manuscripts'); + this.props.fetchResults(this.props.resultClass); } } if (prevProps.search.sortBy != this.props.search.sortBy) { this.props.updatePage(0); if (this.props.search.page == 0) { - this.props.fetchResults('manuscripts'); + this.props.fetchResults(this.props.resultClass); } } if (prevProps.search.sortDirection != this.props.search.sortDirection) { - this.props.fetchResults('manuscripts'); + this.props.fetchResults(this.props.resultClass); } } @@ -121,152 +100,24 @@ class ResultTable extends React.Component { } } - stringListRenderer = cell => { - if (cell == null || cell === '-'){ - return '-'; - } - if (Array.isArray(cell)) { - cell = cell.sort(); - return ( - <ul className={this.props.classes.valueList}> - {cell.map((item, i) => <li key={i}>{item}</li>)} - </ul> - ); - } else { - return <span>{cell}</span>; - } - }; - - objectListRenderer = (cell, makeLink, ordered) => { - if (cell == null || cell === '-'){ - return '-'; - } - else if (Array.isArray(cell)) { - cell = orderBy(cell, 'prefLabel'); - const listItems = cell.map((item, i) => - <li key={i}> - {makeLink && - <a - target='_blank' rel='noopener noreferrer' - href={item.dataProviderUrl} - > - {item.prefLabel} - </a> - } - {!makeLink && item.prefLabel} - </li> - ); - if (ordered) { - return ( - <ol className={this.props.classes.valueList}> - {listItems} - </ol> - ); - } else { - return ( - <ul className={this.props.classes.valueList}> - {listItems} - </ul> - ); - } - } else if (makeLink) { - return ( - <a - target='_blank' rel='noopener noreferrer' - href={cell.dataProviderUrl} - > - {cell.prefLabel} - </a> - ); - } else { - return ( - <span>{cell.prefLabel}</span> - ); - } - }; - - eventRenderer = cell => { - if (cell == null || cell === '-'){ - return '-'; - } - if (Array.isArray(cell)) { - cell = orderBy(cell, 'date'); - const items = cell.map((item, i) => { - return ( - <li key={i}> - {item.date == null ? <span className={this.props.classes.noDate}>No date</span> : item.date} - {' '} - <a - target='_blank' rel='noopener noreferrer' - href={item.dataProviderUrl} - > - {item.type === 'http://www.cidoc-crm.org/cidoc-crm/E8_Acquisition' ? 'Acquisition' : 'Observation'} - </a> - </li> - ); - }); - return ( - <ul className={this.props.classes.valueList}> - {items} - </ul> - ); - } else { - return ( - <span> - {cell.date == null ? <span className={this.props.classes.noDate}>No date</span> : cell.date} - {' '} - <a - target='_blank' rel='noopener noreferrer' - href={cell.dataProviderUrl} - > - {cell.type === 'http://www.cidoc-crm.org/cidoc-crm/E8_Acquisition' ? 'Acquisition' : 'Observation'} - </a> - </span> - - ); - } - }; - - ownerRenderer = cell => { - if (cell == null || cell === '-'){ - return '-'; - } - if (Array.isArray(cell)) { - if (!has(cell[0], 'order')) { - return this.objectListRenderer(cell, true, false); - } - cell.map(item => { - Array.isArray(item.order) ? item.earliestOrder = item.order[0] : item.earliestOrder = item.order; - }); - cell.sort((a, b) => a.earliestOrder - b.earliestOrder); - - const items = cell.map((item, i) => { - return ( - <li key={i}> - <span>{Array.isArray(item.order) ? item.order.toString() : item.order}. </span> - <a - target='_blank' rel='noopener noreferrer' - href={item.dataProviderUrl} - > - {item.prefLabel} - </a> - </li> - ); - }); - return ( - <ul className={this.props.classes.valueListNoBullets}> - {items} - </ul> - ); - } else { - if (!has(cell, 'order')) { - return this.objectListRenderer(cell, true, false); - } - return ( - <span>{cell.date}<br />{cell.location}</span> - ); - } - }; + rowRenderer = row => { + //console.log(this.props.columns) + return ( + <TableRow key={row.id}> + {this.props.columns.map(column => { + return ( + <ResultTableCell + key={column.id} + data={row[column.id] == null ? '-' : row[column.id]} + valueType={column.valueType} + makeLink={column.makeLink} + sortValues={column.sortValues} + /> + ); + })} + </TableRow> + ); + } render() { const { classes } = this.props; @@ -275,7 +126,7 @@ class ResultTable extends React.Component { if (fetchingResults) { return ( <div className={classes.progressContainer}> - <Typography className={classes.progressTitle} variant="h4" color='primary'>Fetching manuscript data</Typography> + <Typography className={classes.progressTitle} variant="h4" color='primary'>Fetching data</Typography> <CircularProgress style={{ color: purple[500] }} thickness={5} /> </div> ); @@ -295,40 +146,7 @@ class ResultTable extends React.Component { routeProps={this.props.routeProps} /> <TableBody> - {results.map(row => { - return ( - <TableRow key={row.id}> - <TableCell> - {this.objectListRenderer(row.source, true)} - </TableCell> - <TableCell className={classes.wideColumn} > - {this.stringListRenderer(row.prefLabel)} - </TableCell> - <TableCell> - {this.objectListRenderer(row.author, true)} - </TableCell> - <TableCell> - {this.objectListRenderer(row.productionPlace, true)} - </TableCell> - <TableCell className={classes.wideColumn}> - {this.objectListRenderer(row.timespan)} - </TableCell> - <TableCell> - {this.stringListRenderer(row.language)} - </TableCell> - {/*<TableCell className={classes.withFilter}> - {this.stringListRenderer(row.material)} - </TableCell>*/} - <TableCell className={classes.wideColumn}> - {this.eventRenderer(row.event)} - </TableCell> - <TableCell className={classes.wideColumn}> - {this.ownerRenderer(row.owner)} - </TableCell> - - </TableRow> - ); - })} + {results.map(row => this.rowRenderer(row))} </TableBody> </Table> </div> @@ -339,6 +157,8 @@ class ResultTable extends React.Component { ResultTable.propTypes = { classes: PropTypes.object.isRequired, + resultClass: PropTypes.string.isRequired, + columns: PropTypes.array.isRequired, search: PropTypes.object.isRequired, facetFilters: PropTypes.object.isRequired, fetchResults: PropTypes.func.isRequired, diff --git a/src/client/components/ResultTableCell.js b/src/client/components/ResultTableCell.js new file mode 100644 index 0000000000000000000000000000000000000000..8506b659ad18868be3e5544d782e42ae944b612b --- /dev/null +++ b/src/client/components/ResultTableCell.js @@ -0,0 +1,235 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { orderBy, has } from 'lodash'; +import TableCell from '@material-ui/core/TableCell'; +import { withStyles } from '@material-ui/core/styles'; + +const styles = () => ({ + tableContainer: { + //marginTop: 72, + overflow: 'auto', + width: '100%', + height: 'calc(100% - 72px)' + }, + table: { + //marginTop: 72, + //minWidth: 700, + //overflowX: 'auto', + //backgroundColor: theme.palette.background.paper + }, + paginationRow: { + borderBottom: '1px solid lightgrey' + }, + valueList: { + paddingLeft: 15 + }, + valueListNoBullets: { + listStyle: 'none', + paddingLeft: 0 + }, + withFilter: { + //minWidth: 170 + }, + wideColumn: { + minWidth: 170 + }, + infoIcon: { + paddingTop: 15 + }, + progressContainer: { + width: '100%', + height: 'calc(100% - 72px)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + progressTitle: { + marginRight: 15 + }, + noDate: { + marginRight: 20 + } +}); + +const ResultTableCell = props => { + + const stringListRenderer = cell => { + if (cell == null || cell === '-'){ + return '-'; + } + if (Array.isArray(cell)) { + cell = cell.sort(); + return ( + <ul className={props.classes.valueList}> + {cell.map((item, i) => <li key={i}>{item}</li>)} + </ul> + ); + } else { + return <span>{cell}</span>; + } + }; + + const objectListRenderer = (cell, makeLink, ordered) => { + if (cell == null || cell === '-'){ + return '-'; + } + else if (Array.isArray(cell)) { + cell = orderBy(cell, 'prefLabel'); + const listItems = cell.map((item, i) => + <li key={i}> + {makeLink && + <a + target='_blank' rel='noopener noreferrer' + href={item.dataProviderUrl} + > + {item.prefLabel} + </a> + } + {!makeLink && item.prefLabel} + </li> + ); + if (ordered) { + return ( + <ol className={props.classes.valueList}> + {listItems} + </ol> + ); + } else { + return ( + <ul className={props.classes.valueList}> + {listItems} + </ul> + ); + } + } else if (makeLink) { + return ( + <a + target='_blank' rel='noopener noreferrer' + href={cell.dataProviderUrl} + > + {cell.prefLabel} + </a> + ); + } else { + return ( + <span>{cell.prefLabel}</span> + ); + } + }; + + const eventRenderer = cell => { + if (cell == null || cell === '-'){ + return '-'; + } + if (Array.isArray(cell)) { + cell = orderBy(cell, 'date'); + const items = cell.map((item, i) => { + return ( + <li key={i}> + {item.date == null ? <span className={props.classes.noDate}>No date</span> : item.date} + {' '} + <a + target='_blank' rel='noopener noreferrer' + href={item.dataProviderUrl} + > + {item.type === 'http://www.cidoc-crm.org/cidoc-crm/E8_Acquisition' ? 'Acquisition' : 'Observation'} + </a> + </li> + ); + }); + return ( + <ul className={props.classes.valueList}> + {items} + </ul> + ); + } else { + return ( + <span> + {cell.date == null ? <span className={props.classes.noDate}>No date</span> : cell.date} + {' '} + <a + target='_blank' rel='noopener noreferrer' + href={cell.dataProviderUrl} + > + {cell.type === 'http://www.cidoc-crm.org/cidoc-crm/E8_Acquisition' ? 'Acquisition' : 'Observation'} + </a> + </span> + + ); + } + }; + + const ownerRenderer = cell => { + if (cell == null || cell === '-'){ + return '-'; + } + if (Array.isArray(cell)) { + if (!has(cell[0], 'order')) { + return objectListRenderer(cell, true, false); + } + cell.map(item => { + Array.isArray(item.order) ? item.earliestOrder = item.order[0] : item.earliestOrder = item.order; + }); + cell.sort((a, b) => a.earliestOrder - b.earliestOrder); + + const items = cell.map((item, i) => { + return ( + <li key={i}> + <span>{Array.isArray(item.order) ? item.order.toString() : item.order}. </span> + <a + target='_blank' rel='noopener noreferrer' + href={item.dataProviderUrl} + > + {item.prefLabel} + </a> + </li> + ); + }); + return ( + <ul className={props.classes.valueListNoBullets}> + {items} + </ul> + ); + } else { + if (!has(cell, 'order')) { + return objectListRenderer(cell, true, false); + } + return ( + <span>{cell.date}<br />{cell.location}</span> + ); + } + }; + + const { data, valueType, makeLink, sortValues } = props; + let renderer = null; + switch (valueType) { + case 'object': + renderer = objectListRenderer; + break; + case 'string': + renderer = stringListRenderer; + break; + case 'event': + renderer = eventRenderer; + break; + case 'owner': + renderer = ownerRenderer; + break; + } + + return( + <TableCell> + {renderer(data, makeLink, sortValues)} + </TableCell> + ); +}; + +ResultTableCell.propTypes = { + classes: PropTypes.object.isRequired, + data: PropTypes.oneOfType([PropTypes.object, PropTypes.array, PropTypes.string]).isRequired, + valueType: PropTypes.string.isRequired, + makeLink: PropTypes.bool.isRequired, + sortValues: PropTypes.bool.isRequired, +}; + +export default withStyles(styles)(ResultTableCell); diff --git a/src/client/components/Works.js b/src/client/components/Works.js new file mode 100644 index 0000000000000000000000000000000000000000..fd3f8c03f5d3be180f33780d5a700669e68c5fbb --- /dev/null +++ b/src/client/components/Works.js @@ -0,0 +1,44 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Route, Redirect } from 'react-router-dom'; +import ViewTabs from './ViewTabs'; +import ResultTable from './ResultTable'; + +let Works = props => { + return ( + <React.Fragment> + <ViewTabs routeProps={props.routeProps} /> + <Route + exact path='/works' + render={() => <Redirect to='works/table' />} + /> + <Route + path={'/works/table'} + render={routeProps => + <ResultTable + resultClass='manuscripts' + search={props.search} + facetFilters={props.facetFilters} + fetchResults={props.fetchResults} + updatePage={props.updatePage} + sortResults={props.sortResults} + routeProps={routeProps} + /> + } + /> + </React.Fragment> + ); +}; + +Works.propTypes = { + search: PropTypes.object.isRequired, + facetFilters: PropTypes.object.isRequired, + fetchResults: PropTypes.func.isRequired, + fetchPlaces: PropTypes.func.isRequired, + fetchPlace: PropTypes.func.isRequired, + updatePage: PropTypes.func.isRequired, + sortResults: PropTypes.func.isRequired, + routeProps: PropTypes.object.isRequired +}; + +export default Works; diff --git a/src/client/containers/MapApp.js b/src/client/containers/MapApp.js index 2b7ba4e6ea93f3c38d8bb688454df1158d3d9377..c5a630b7437f5a78a5f47386020f5e24aef84bf0 100644 --- a/src/client/containers/MapApp.js +++ b/src/client/containers/MapApp.js @@ -9,6 +9,7 @@ import TopBar from '../components/TopBar'; import Footer from '../components/Footer'; import { BrowserRouter as Router, Route } from 'react-router-dom'; import Manuscripts from '../components/Manuscripts'; +import Works from '../components/Works'; import Main from '../components/Main'; import FacetBar from '../components/FacetBar'; import Grid from '@material-ui/core/Grid'; @@ -119,6 +120,40 @@ let MapApp = (props) => { </React.Fragment> } /> + <Route + path="/works" + render={routeProps => + <React.Fragment> + <Grid item sm={12} md={3} className={classes.facetBarContainer}> + <FacetBar + facetFilters={props.facet.facetFilters} + source={props.facet.source} + author={props.facet.author} + language={props.facet.language} + productionPlace={props.facet.productionPlace} + fetchFacet={props.fetchFacet} + updateFilter={props.updateFilter} + updatedFacet={props.facet.updatedFacet} + /> + </Grid> + <Grid item sm={12} md={9} className={classes.resultsContainer}> + <Paper className={classes.resultsContainerPaper}> + <Works + search={props.search} + facetFilters={props.facet.facetFilters} + fetchResults={props.fetchResults} + fetchPlaces={props.fetchPlaces} + fetchPlace={props.fetchPlace} + updatePage={props.updatePage} + sortResults={props.sortResults} + routeProps={routeProps} + /> + </Paper> + </Grid> + </React.Fragment> + } + /> + </Grid> </React.Fragment> </Router> diff --git a/src/client/reducers/search.js b/src/client/reducers/search.js index fd9133c30a79a3db76296af40e293cb88adb4090..39ad15c4eb2762ee7602547835810412ea4df00a 100644 --- a/src/client/reducers/search.js +++ b/src/client/reducers/search.js @@ -12,6 +12,62 @@ import { export const INITIAL_STATE = { resultCount: 0, results: [], + resultTableColumns: [ + { + id: 'source', + valueType: 'object', + makeLink: true, + sortValues: false + }, + { + id: 'prefLabel', + valueType: 'string', + makeLink: false, + sortValues: true + }, + { + id: 'author', + valueType: 'object', + makeLink: true, + sortValues: true + }, + { + id: 'productionPlace', + valueType: 'object', + makeLink: true, + sortValues: true + }, + { + id: 'timespan', + valueType: 'object', + makeLink: true, + sortValues: false + }, + { + id: 'language', + valueType: 'string', + makeLink: true, + sortValues: true + }, + // { + // id: 'material', + // valueType: 'string', + // makeLink: true, + // sortValues: true + // }, + { + id: 'event', + valueType: 'event', + makeLink: true, + sortValues: true + }, + { + id: 'owner', + valueType: 'owner', + makeLink: true, + sortValues: true + } + ], places: [], place: {}, page: -1,