Skip to content

Commit

Permalink
BASIRA #292 - Adding "/places" and "/people" list pages
Browse files Browse the repository at this point in the history
  • Loading branch information
dleadbetter committed Dec 24, 2024
1 parent 0582e34 commit ed0cb87
Show file tree
Hide file tree
Showing 10 changed files with 333 additions and 87 deletions.
82 changes: 47 additions & 35 deletions client/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@

import { useDragDrop } from '@performant-software/shared-components';
import React from 'react';
import { Route, BrowserRouter as Router } from 'react-router-dom';
import { Route, BrowserRouter as Router, Switch } from 'react-router-dom';
import Admin from './pages/admin/Admin';
import Artwork from './pages/Artwork';
import AuthenticatedRoute from './components/AuthenticatedRoute';
import Document from './pages/Document';
import Home from './pages/Home';
import NotFound404 from './pages/404';
import People from './pages/People';
import Person from './pages/Person';
import PhysicalComponent from './pages/PhysicalComponent';
import Place from './pages/Place';
import Places from './pages/Places';
import Search from './pages/Search';
import SearchContextProvider from './components/SearchContextProvider';
import SearchHistory from './pages/SearchHistory';
Expand All @@ -21,40 +23,50 @@ import './App.css';
const App = () => (
<Router>
<SearchContextProvider>
<Route
path='/'
component={Search}
exact
/>
<Route
path='/search_history'
component={SearchHistory}
exact
/>
<Route
path='/artworks/:id'
component={Artwork}
/>
<Route
path='/documents/:id'
component={Document}
/>
<Route
path='/people/:id'
component={Person}
/>
<Route
path='/physical_components/:id'
component={PhysicalComponent}
/>
<Route
path='/places/:id'
component={Place}
/>
<Route
path='/visual_contexts/:id'
component={VisualContext}
/>
<Switch>
<Route
path='/'
component={Search}
exact
/>
<Route
path='/search_history'
component={SearchHistory}
exact
/>
<Route
path='/artworks/:id'
component={Artwork}
/>
<Route
path='/documents/:id'
component={Document}
/>
<Route
path='/people/:id'
component={Person}
/>
<Route
path='/people'
component={People}
/>
<Route
path='/physical_components/:id'
component={PhysicalComponent}
/>
<Route
path='/places/:id'
component={Place}
/>
<Route
path='/places'
component={Places}
/>
<Route
path='/visual_contexts/:id'
component={VisualContext}
/>
</Switch>
</SearchContextProvider>
<Route
path='/404'
Expand Down
52 changes: 52 additions & 0 deletions client/src/components/ListPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// @flow

import { ListTable } from '@performant-software/semantic-components';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Link, useLocation } from 'react-router-dom';
import { Container } from 'semantic-ui-react';
import NavBar from './NavBar';

type Props = {
className?: string,
collectionName: string,
columns: Array<any>,
onLoad: (params: any) => Promise<any>
};

const ListPage = (props: Props) => {
const location = useLocation();
const { t } = useTranslation();

return (
<Container
className='list-page'
fluid
>
<NavBar />
<Container>
<ListTable
actions={[{
as: Link,
asProps: (item) => ({
to: `${location.pathname}/${item.id}`
}),
name: 'navigate',
icon: 'arrow alternate circle right outline',
popup: {
content: t('ListPage.actions.navigate.content'),
title: t('ListPage.actions.navigate.title')
}
}]}
className={props.className}
collectionName={props.collectionName}
columns={props.columns}
onLoad={props.onLoad}
searchable
/>
</Container>
</Container>
);
};

export default ListPage;
25 changes: 23 additions & 2 deletions client/src/components/NavBar.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,44 @@
// @flow

import React from 'react';
import { useTranslation } from 'react-i18next';
import { Link, useRouteMatch } from 'react-router-dom';
import { Header, Menu } from 'semantic-ui-react';
import LinksMenu from './LinksMenu';
import './NavBar.css';

const NavBar = () => {
const { t } = useTranslation();

return (
<Menu
className='nav-bar'
inverted
size='large'
>
<Menu.Item>
<Menu.Item
as={Link}
to='/'
>
<Header
content='BASIRA'
content={t('NavBar.title')}
inverted
/>
</Menu.Item>
<Menu.Menu>
<Menu.Item
active={useRouteMatch('/people')}
as={Link}
content={t('NavBar.menu.creators')}
to='people'
/>
<Menu.Item
active={useRouteMatch('/places')}
as={Link}
content={t('NavBar.menu.repositories')}
to='places'
/>
</Menu.Menu>
<LinksMenu
position='right'
/>
Expand Down
21 changes: 13 additions & 8 deletions client/src/components/RecordPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import React, {
type Element,
type Node
} from 'react';
import { Link } from 'react-router-dom';
import { Link, useLocation } from 'react-router-dom';
import {
Button,
Container,
Expand Down Expand Up @@ -38,10 +38,13 @@ type Props = {

const RecordPage = (props: Props) => {
const [sidebarVisible, setSidebarVisible] = useState(false);
const location = useLocation();

const menuBarRef = useRef(null);
const { height: minHeight } = useSidebar(menuBarRef);

const { state: { fromSearch } = {} } = location;

return (
<Container
className={classNames('record-page', props.className)}
Expand Down Expand Up @@ -78,13 +81,15 @@ const RecordPage = (props: Props) => {
{ props.renderTitle() }
</Menu.Item>
)}
<Menu.Item
position='right'
>
<SearchLink
inverted
/>
</Menu.Item>
{ fromSearch && (
<Menu.Item
position='right'
>
<SearchLink
inverted
/>
</Menu.Item>
)}
</Menu>
</Ref>
<Sidebar.Pushable
Expand Down
52 changes: 52 additions & 0 deletions client/src/hooks/Qualifiable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// @flow

import { useCallback, useMemo } from 'react';
import _ from 'underscore';
import Qualifiables from '../utils/Qualifiables';

const useQualifiable = (attributes) => {
/**
* Adds the `resolve` attribute for any column with `object` and `group` attributes.
*
* @type {[]}
*/
const columns = useMemo(() => {
const value = [];

_.each(attributes, (c) => {
const column = { ...c };

if (column.object && column.group) {
const resolve = (item) => Qualifiables.getValueListValue(item, column.object, column.group);
_.extend(column, { resolve })
}

value.push(column);
});

return value;
}, [attributes]);

/**
* Adds the `sort_by_object` and `sort_by_group` parameters if the current sort column
* contains `object` and `group` attributes.
*
* @type {function(*): *}
*/
const getParameters = useCallback((params) => {
const sortColumn = _.findWhere(columns, { name: params.sort_by });

if (sortColumn && sortColumn.object && sortColumn.group) {
_.extend(params, { sort_by_object: sortColumn.object, sort_by_group: sortColumn.group });
}

return params;
}, [columns]);

return {
columns,
getParameters
};
};

export default useQualifiable;
15 changes: 15 additions & 0 deletions client/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,14 @@
"contribute": "Contribute Content"
}
},
"ListPage": {
"actions": {
"navigate": {
"content": "Navigate to the detail page for this record",
"title": "Navigate"
}
}
},
"LocationModal": {
"labels": {
"artwork": "Artwork",
Expand All @@ -328,6 +336,13 @@
"viewInstitution": "View Institution"
}
},
"NavBar": {
"menu": {
"creators": "Creators",
"repositories": "Repositories"
},
"title": "BASIRA"
},
"NotesModal": {
"title": "Internal Notes"
},
Expand Down
38 changes: 38 additions & 0 deletions client/src/pages/People.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// @flow

import React from 'react';
import { useTranslation } from 'react-i18next';
import ListPage from '../components/ListPage';
import PeopleService from '../services/People';
import useQualifiable from '../hooks/Qualifiable';

const People = () => {
const { t } = useTranslation();

const { columns, getParameters } = useQualifiable([{
name: 'display_name',
label: t('People.columns.name'),
sortable: true
}, {
name: 'person_type',
label: t('People.columns.type'),
sortable: true
}, {
name: 'nationality',
label: t('People.columns.nationality'),
object: 'Person',
group: 'Nationality',
sortable: true
}]);

return (
<ListPage
className='people'
collectionName='people'
columns={columns}
onLoad={(params) => PeopleService.fetchAll(getParameters(params))}
/>
);
};

export default People;
Loading

0 comments on commit ed0cb87

Please sign in to comment.