Commit b0a974cf authored by Oleksii Savran's avatar Oleksii Savran

Audit logs for Accession and Cooperator

parent 359d0805
......@@ -5,13 +5,14 @@ import { AxiosInstance, AxiosRequestConfig } from 'axios';
import Accession from '@gringlobal/client/model/gringlobal/Accession';
import AccessionFilter from '@gringlobal/client/model/gringlobal/AccessionFilter';
import { IPageRequest, FilteredPage } from '@gringlobal/client/model/page';
import { IPageRequest, FilteredPage, Page } from '@gringlobal/client/model/page';
import { dereferenceReferences3 } from '@gringlobal/client/utilities';
import AccessionSource from '@gringlobal/client/model/gringlobal/AccessionSource';
import AccessionInvGroup from '@gringlobal/client/model/gringlobal/AccessionInvGroup';
import AccessionDetails from '@gringlobal/client/model/gringlobal/AccessionDetails';
import AccessionAction from '@gringlobal/client/model/gringlobal/AccessionAction';
import AccessionActionFilter from '@gringlobal/client/model/gringlobal/AccessionActionFilter';
import AuditLog from '@gringlobal/client/model/gringlobal/AuditLog';
const URL_UPDATE = '/api/v1/a';
const URL_CREATE = '/api/v1/a';
......@@ -22,6 +23,7 @@ const URL_ADD_MATERIAL = '/api/v1/a/acquire';
const URL_DETAILS = UrlTemplate.parse('/api/v1/a/details/{id}');
const URL_OVERVIEW = UrlTemplate.parse('/api/v1/a/overview/{groupBy}');
const URL_LIST_ACTIONS = '/api/v1/a/action/list';
const URL_ACCESSION_AUDIT_LOGS = UrlTemplate.parse('/api/v1/a/auditlog/{id}');
/**
* Accession service
......@@ -246,6 +248,33 @@ class AccessionService {
});
return data as FilteredPage<AccessionAction>;
});
};
/**
* accessionAuditLogs at /api/v1/a/auditlog/{id}
*
* @param id undefined
* @param page the page
* @param xhrConfig additional xhr config
*/
public accessionAuditLogs = (id: number, page: IPageRequest = {}, xhrConfig?: AxiosRequestConfig): Promise<Page<AuditLog>> => {
const qs = QueryString.stringify({
p: page.page || undefined,
l: page.size || undefined,
d: page.direction ? page.direction : undefined,
s: page.properties || undefined,
}, {});
const apiUrl = URL_ACCESSION_AUDIT_LOGS.expand({ id }) + (qs ? `?${qs}` : '');
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return this._axios.request({
...xhrConfig,
url: apiUrl,
method: 'GET',
...content,
}).then(({ data }) => data as Page<AuditLog>);
}
}
......
......@@ -7,12 +7,14 @@ import { dereferenceReferences3 } from '@gringlobal/client/utilities';
import Cooperator from '@gringlobal/client/model/gringlobal/Cooperator';
import { FilteredPage, Page, IPageRequest } from '@gringlobal/client/model/page';
import CooperatorFilter from '@gringlobal/client/model/gringlobal/CooperatorFilter';
import AuditLog from '@gringlobal/client/model/gringlobal/AuditLog';
const URL_UPDATE_COOPERATOR = '/api/v1/cooperator';
const URL_CREATE_COOPERATOR = '/api/v1/cooperator';
const URL_FILTER = '/api/v1/cooperator/filter';
const URL_LIST = '/api/v1/cooperator/list';
const URL_GET_COOPERATOR = UrlTemplate.parse('/api/v1/cooperator/{id}');
const URL_COOPERATOR_AUDIT_LOGS = UrlTemplate.parse('/api/v1/cooperator/auditlog/{id}');
/**
* Cooperator service
......@@ -29,7 +31,7 @@ class CooperatorService {
/**
* filter at /api/v1/cooperator/filter
*
* @param f undefined
* @param filter undefined
* @param page undefined
* @param xhrConfig additional xhr config
*/
......@@ -65,7 +67,7 @@ class CooperatorService {
/**
* list at /api/v1/cooperator/list
*
* @param f undefined
* @param filter undefined
* @param page undefined
* @param xhrConfig additional xhr config
*/
......@@ -155,6 +157,33 @@ class CooperatorService {
...content,
}).then(({ data }) => data as Cooperator);
};
/**
* cooperatorAuditLogs at /api/v1/cooperator/auditlog/{id}
*
* @param id undefined
* @param page the page
* @param xhrConfig additional xhr config
*/
public cooperatorAuditLogs = (id: number, page: IPageRequest = {}, xhrConfig?: AxiosRequestConfig): Promise<Page<AuditLog>> => {
const qs = QueryString.stringify({
p: page.page || undefined,
l: page.size || undefined,
d: page.direction ? page.direction : undefined,
s: page.properties || undefined,
}, {});
const apiUrl = URL_COOPERATOR_AUDIT_LOGS.expand({ id }) + (qs ? `?${qs}` : '');
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return this._axios.request({
...xhrConfig,
url: apiUrl,
method: 'GET',
...content,
}).then(({ data }) => data as Page<AuditLog>);
}
}
export default CooperatorService;
......@@ -141,6 +141,8 @@
}
},
"details": {
"auditLogs": "Audit Logs",
"actions": "Actions",
"title": "Accession",
"attachments": "Attachments",
"quarantine": "Accession quarantine",
......@@ -184,6 +186,7 @@
"title": "Cooperators"
},
"details": {
"auditLogs": "Audit Logs",
"title": "Cooperator",
"auditingInformation": "Auditing information",
"organization": "Organization",
......
......@@ -13,6 +13,8 @@
}
},
"details": {
"auditLogs": "Audit Logs",
"actions": "Actions",
"title": "Accession",
"attachments": "Attachments",
"quarantine": "Accession quarantine",
......
......@@ -5,10 +5,8 @@ import { WithTranslation, withTranslation } from 'react-i18next';
// Action
import { ApiCall } from '@gringlobal/client/model/common';
import { getAccessionAction, getAccessionInventories, uploadAccessionAttachment } from 'accession/action/public';
import Tab from '@material-ui/core/Tab';
import HeaderTabs from '@gringlobal/client/ui/common/tabs/HeaderTabs';
import TabPanel from '@gringlobal/client/ui/common/tabs/TabPanel';
// Service
import { AccessionService } from '@gringlobal/client/service';
// Models
import AccessionDetails from '@gringlobal/client/model/gringlobal/AccessionDetails';
import Inventory from '@gringlobal/client/model/gringlobal/Inventory';
......@@ -17,7 +15,7 @@ import AccessionSource from '@gringlobal/client/model/gringlobal/AccessionSource
import AccessionIpr from '@gringlobal/client/model/gringlobal/AccessionIpr';
import AccessionAction from '@gringlobal/client/model/gringlobal/AccessionAction';
import Accession from '@gringlobal/client/model/gringlobal/Accession';
import { FilteredPage } from '@gringlobal/client/model/page';
import { FilteredPage, SortDirection, Page } from '@gringlobal/client/model/page';
// UI
import Loading from '@gringlobal/client/ui/common/Loading';
import { Card, CardContent, CardHeader, CardActions, Button } from '@material-ui/core';
......@@ -33,6 +31,10 @@ import PageTitle from '@gringlobal/client/ui/common/PageTitle';
import FileUploader from '@gringlobal/client/ui/common/file-uploader';
import AttachmentsDisplay from 'repository/ui/c/AttachmentsDisplay';
import AuditDataDisplay from 'common/AuditDataDisplay';
import { AuditLogsTable } from 'common/AuditLogsTable';
import Tab from '@material-ui/core/Tab';
import HeaderTabs from '@gringlobal/client/ui/common/tabs/HeaderTabs';
import TabPanel from '@gringlobal/client/ui/common/tabs/TabPanel';
const InventoryTableConfig = new TableConfiguration(TableConfiguration.merge(
......@@ -66,6 +68,14 @@ interface IDetailsPageProps extends React.ClassAttributes<any>, WithTranslation
uploadAccessionAttachment: (id: number, file: File) => void;
}
enum AccessionDetailsTabs {
INFO = 'info',
ACTIONS = 'actions',
INVENTORIES = 'inventories',
ATTACHMENTS = 'attachments',
AUDITLOGS = 'auditlogs',
}
class AccessionDetailsPage extends React.Component<IDetailsPageProps> {
protected static needs = [
......@@ -74,7 +84,8 @@ class AccessionDetailsPage extends React.Component<IDetailsPageProps> {
];
public state = {
selectedTab: 'info',
selectedTab: AccessionDetailsTabs.INFO,
auditLogs: null,
};
public constructor(props) {
......@@ -101,6 +112,13 @@ class AccessionDetailsPage extends React.Component<IDetailsPageProps> {
this.setState({
selectedTab: newValue,
});
if (newValue === AccessionDetailsTabs.AUDITLOGS) {
if (!this.state.auditLogs) {
AccessionService.accessionAuditLogs(+this.props.id)
.then((auditLogs) => this.setState({ auditLogs }));
}
}
}
private handleUploading = (files: File[]) => {
......@@ -111,9 +129,20 @@ class AccessionDetailsPage extends React.Component<IDetailsPageProps> {
}
};
private onSortChange = (sortBy: string, dir: SortDirection): void => {
AccessionService.accessionAuditLogs(+this.props.id, { properties: [sortBy], direction: [dir] })
.then((auditLogs) => this.setState({ auditLogs }));
};
private loadMore = (): void => {
const { auditLogs } = this.state;
AccessionService.accessionAuditLogs(+this.props.id, Page.nextPage(auditLogs))
.then((newAuditLogs) => this.setState({ auditLogs: Page.merge(auditLogs, newAuditLogs) }));
};
public render(): React.ReactNode {
const { inventories, accessionCall, t } = this.props;
const { selectedTab } = this.state;
const { selectedTab, auditLogs } = this.state;
const columns = InventoryTableConfig.getColumns(inventories && inventories.data && inventories.data.content ? inventories.data.content[0] : null);
if (!accessionCall) {
return null;
......@@ -132,16 +161,16 @@ class AccessionDetailsPage extends React.Component<IDetailsPageProps> {
scrollButtons="auto"
aria-label="Inventory tabs"
>
<Tab value="info" label={ accession ? <>{ accession.accessionNumber }</> : t('accession.public.p.details.title') } />
<Tab value="inventories" label="Inventories" />
<Tab value="attachments" label="Attachments" />
<Tab value="actions" label="Actions" />
{/* <Tab value="other" label="Other" /> */}
<Tab value={ AccessionDetailsTabs.INFO } label={ accession ? <>{ accession.accessionNumber }</> : t('accession.public.p.details.title') } />
<Tab value={ AccessionDetailsTabs.INVENTORIES } label={ t('client:model.name.Inventory_plural') } />
<Tab value={ AccessionDetailsTabs.ATTACHMENTS } label={ t('accession.public.p.details.attachments') } />
<Tab value={ AccessionDetailsTabs.ACTIONS } label={ t('accession.public.p.details.actions') } />
<Tab value={ AccessionDetailsTabs.AUDITLOGS } label={ t('accession.public.p.details.auditLogs') } />
</HeaderTabs>
{ loading && <Loading/> }
{ accession &&
<>
<TabPanel value={ selectedTab } index="info">
<TabPanel value={ selectedTab } index={ AccessionDetailsTabs.INFO }>
<>
<Card>
<CardHeader
......@@ -336,7 +365,7 @@ class AccessionDetailsPage extends React.Component<IDetailsPageProps> {
</>
</TabPanel>
<TabPanel value={ selectedTab } index="inventories">
<TabPanel value={ selectedTab } index={ AccessionDetailsTabs.INVENTORIES }>
<Table
noWrap
tableKey="accession-inventories-list"
......@@ -350,7 +379,7 @@ class AccessionDetailsPage extends React.Component<IDetailsPageProps> {
/>
</TabPanel>
<TabPanel value={ selectedTab } index="attachments">
<TabPanel value={ selectedTab } index={ AccessionDetailsTabs.ATTACHMENTS }>
<Card>
<CardHeader title={ t('accession.public.p.details.attachments') } />
{ accession.attachments && <AttachmentsDisplay attachments={ accession.attachments }/> }
......@@ -361,7 +390,7 @@ class AccessionDetailsPage extends React.Component<IDetailsPageProps> {
</TabPanel>
<TabPanel value={ selectedTab } index="actions">
<TabPanel value={ selectedTab } index={ AccessionDetailsTabs.ACTIONS }>
<>
<Card>
{ accession.actions && accession.actions.sort(AccessionAction.completedDateSort).map((action) => (
......@@ -385,8 +414,18 @@ class AccessionDetailsPage extends React.Component<IDetailsPageProps> {
</>
</TabPanel>
<TabPanel value={ selectedTab } index="other">
<TabPanel value={ selectedTab } index={ AccessionDetailsTabs.AUDITLOGS }>
<>
{ !auditLogs && <Loading/> }
{ auditLogs && auditLogs.content.length > 0 &&
<AuditLogsTable
auditLogs={ auditLogs.content }
sort={ auditLogs.sort }
total={ auditLogs.totalElements }
onSortChange={ this.onSortChange }
loadMore={ this.loadMore }
/>
}
</>
</TabPanel>
</>
......
......@@ -20,6 +20,7 @@ const InventoryAuditLogsTableConfigProps = {
previousState: { sort: null },
newState: { sort: null },
createdBy: { sort: null },
property: { sort: null },
},
columnsRenderers: {
logDate: Renderers.DATE_RENDERER,
......@@ -29,14 +30,14 @@ const InventoryAuditLogsTableConfigProps = {
const className = row.classPk.classname.split('.').pop(); // split using dots and get the last element of array
const key = 'propertyName';
const propertyName = row[key];
return propertyName ? t(`client:model.${className}.${propertyName}`) : null
return propertyName ? t([`client:model.${className}.${propertyName}`, `client:model._.${propertyName}`]) : null
},
},
};
const InventoryAuditLogsTableConfig = new TableConfiguration(InventoryAuditLogsTableConfigProps as any);
const AuditLogsTableConfig = new TableConfiguration(InventoryAuditLogsTableConfigProps as any);
interface IInventoryAuditLogsTableProps {
interface IAuditLogsTableProps {
auditLogs: AuditLog[];
total: number;
sort: ISort[];
......@@ -44,17 +45,17 @@ interface IInventoryAuditLogsTableProps {
loadMore: () => void;
}
export const InventoryAuditLogsTable = (props: IInventoryAuditLogsTableProps) => {
export const AuditLogsTable = (props: IAuditLogsTableProps) => {
const { auditLogs, total, sort, onSortChange, loadMore } = props;
if (auditLogs && auditLogs.length > 0) {
return (
<Table
noWrap
tableKey="inventory-auditlogs-table"
tableKey="auditlogs-table"
type={ 'AuditLog' }
columns={ InventoryAuditLogsTableConfig.defaultColumns }
columns={ AuditLogsTableConfig.defaultColumns }
data={ auditLogs }
tableConfig={ InventoryAuditLogsTableConfig }
tableConfig={ AuditLogsTableConfig }
sort={ sort }
total={ total }
onSortChange={ onSortChange }
......
......@@ -5,6 +5,7 @@
"title": "Cooperators"
},
"details": {
"auditLogs": "Audit Logs",
"title": "Cooperator",
"auditingInformation": "Auditing information",
"organization": "Organization",
......
......@@ -28,7 +28,7 @@ import ButtonBar from '@gringlobal/client/ui/common/button/ButtonBar';
import { CodeValueDisplay } from 'common/CodeValue';
import PageTitle from '@gringlobal/client/ui/common/PageTitle';
import { BasicInventoryActionsTable as InventoryActionsTable } from 'inventory/ui/c/InventoryActionsTable';
import { InventoryAuditLogsTable } from 'inventory/ui/c/InventoryAuditLogsTable';
import { AuditLogsTable } from 'common/AuditLogsTable';
import AttachmentsDisplay from 'repository/ui/c/AttachmentsDisplay';
import FileUploader from '@gringlobal/client/ui/common/file-uploader';
import AuditDataDisplay from 'common/AuditDataDisplay';
......@@ -427,7 +427,7 @@ class InventoryDetailsPage extends React.Component<IDetailsPageProps> {
{ !auditLogs && <Loading/> }
{
auditLogs && auditLogs.content.length > 0 &&
<InventoryAuditLogsTable
<AuditLogsTable
auditLogs={ auditLogs.content }
sort={ auditLogs.sort }
total={ auditLogs.totalElements }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment