Commit fa537701 authored by Maksym Tishchenko's avatar Maksym Tishchenko
Browse files

Merge branch 'pdf-report-fixes' into 'main'

Generating PDFs: Code cleanup

See merge request grin-global/grin-global-ui!383
parents 0d3224f6 5d14b50a
import * as UrlTemplate from 'url-template';
import * as QueryString from 'query-string';
import { AxiosInstance, AxiosRequestConfig } from 'axios';
import RepositoryFile from "@gringlobal-ce/client/model/repository/RepositoryFile";
const API_PREFIX = '/api/v1'
const URL_GENERATE_REPORT = UrlTemplate.parse(`/report/generate/{reportTemplate}`);
const URL_GET_REPORT_LIST = UrlTemplate.parse(`/report/list`);
const API_PREFIX = '/api/v1/'
const URL_GENERATE_REPORT = UrlTemplate.parse(API_PREFIX + '{endpoint}/report/generate/{reportTemplate}');
const URL_GET_REPORT_LIST = UrlTemplate.parse(API_PREFIX + '{endpoint}/report/list');
/**
* Repository service
......@@ -20,21 +19,20 @@ class RepositoryService {
}
/**
* generateReport_41 at /api/v1{entityPath}/report/generate/{reportTemplate}
* generateReport at /api/v1/{endpoint}/report/generate/{reportTemplate}
*
* @param entityPath
* @param endpoint the API endpoint base (e.g. "order", "a")
* @param reportTemplate undefined
* @param entityIds undefined
* @param xhrConfig additional xhr config
*/
public generateReport = (entityPath: string, reportTemplate: string, entityIds: number[], xhrConfig?: AxiosRequestConfig): Promise<string> => {
public generateReport = (endpoint: string, reportTemplate: string, entityIds: number[], xhrConfig?: AxiosRequestConfig): Promise<unknown> => {
const qs = QueryString.stringify({
entityIds: entityIds || undefined,
}, {});
const apiUrl = API_PREFIX + entityPath + URL_GENERATE_REPORT.expand({ reportTemplate }) + (qs ? `?${qs}` : '');
const apiUrl = URL_GENERATE_REPORT.expand({ endpoint, reportTemplate });
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
const content = {
data: entityIds
};
return this._axios.request({
...xhrConfig,
......@@ -42,21 +40,21 @@ class RepositoryService {
method: 'POST',
responseType: 'blob',
headers: {
'Content-Type': 'application/pdf'
'Accept': 'application/pdf, */*'
},
...content,
}).then(({ data }) => data as string);
}).then(({ data }) => data as unknown);
}
/**
* getReportList at /api/v1{entityPath}/report/list
* getReportList at /api/v1/{endpoint}/report/list
*
* @param entityPath
* @param endpoint
* @param xhrConfig additional xhr config
*/
public getReportList = (entityPath: string, xhrConfig?: AxiosRequestConfig): Promise<RepositoryFile[]> => {
public getReportList = (endpoint: string, xhrConfig?: AxiosRequestConfig): Promise<RepositoryFile[]> => {
const apiUrl = API_PREFIX + entityPath + URL_GET_REPORT_LIST.expand({ entityPath });
const apiUrl = URL_GET_REPORT_LIST.expand({ endpoint });
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
......
......@@ -449,13 +449,12 @@ export function printLabels(labelData: Generator | { [key: string]: any }[], opt
return toPrint;
}
export function printPdfReport(entityPath: string, originalFilename: string, itemIds: number[]): Promise<string> {
return ReportService.generateReport(entityPath, originalFilename, itemIds).then((pdfReport) => {
const printWindow = window.open(URL.createObjectURL(pdfReport));
export function printPdfReport(endpoint: string, originalFilename: string, itemIds: number[]): Promise<unknown> {
return ReportService.generateReport(endpoint, originalFilename, itemIds).then((pdfReport) => {
const printWindow = window.open(URL.createObjectURL(pdfReport as Blob));
if (printWindow) {
printWindow.focus();
}
return pdfReport
return pdfReport;
})
}
......@@ -30,6 +30,7 @@ import {
// Service
import { UISecurity as P, AccessionService, InventoryService, SourceDescObservationService } from '@gringlobal-ce/client/service';
import navigateTo from '@gringlobal-ce/client/action/navigation';
import { showDialog } from '@gringlobal-ce/client/action/dialog';
// Models
import {
......@@ -105,6 +106,7 @@ import { WithBrowsePageBase } from 'ui/common/withBrowsePageBase';
import { SourceDescObservationTableDefaultConfig } from 'accession/ui/SourceDescObservationBrowsePage';
import SourceDescObservationDialog from 'accession/ui/c/SourceDescObservationDialog';
import TranslatedSourceDescObservation from '@gringlobal-ce/client/model/gringlobal/TranslatedSourceDescObservation';
import PrintReportDialog from 'common/PrintReportDialog';
const AccessionSourceTableDefaultConfig = {
defaultColumns: [
......@@ -165,6 +167,7 @@ class AccessionDetailsPage extends React.Component<PropsFromRedux & WithBrowsePa
];
public static defaultTab = AccessionDetailsTabs.INFO;
private static PRINT_DIALOG_KEY = 'accession-print-pdf-dialog';
public state = {
auditLogs: null,
......@@ -694,6 +697,9 @@ class AccessionDetailsPage extends React.Component<PropsFromRedux & WithBrowsePa
<P.HasAccess action={ P.PassportData } permission={ P.delete } siteId={ accession.site.id }>
<Button onClick={ this.handleRemove } variant="text">{ t('common:action.delete') }</Button>
</P.HasAccess>
<Button variant="outlined" color="secondary" onClick={ () => this.props.showDialog(AccessionDetailsPage.PRINT_DIALOG_KEY) }>
{ t('common:action.generatePdf') }
</Button>
</ButtonBar>
</CardActions>
<CardContent>
......@@ -1027,6 +1033,13 @@ class AccessionDetailsPage extends React.Component<PropsFromRedux & WithBrowsePa
sectioned
/>
</P.HasAccess>
<PrintReportDialog
endpoint="a"
modelName="Accession"
selectedItemIds={ [ accession?.id ] }
dialogKey={ AccessionDetailsPage.PRINT_DIALOG_KEY }
/>
</TabPanel>
<TabPanel value={ currentTab } index={ AccessionDetailsTabs.INVENTORIES }>
......@@ -1194,6 +1207,7 @@ const mapDispatch ={
removeAccessionAttachmentAction,
removeAccessionAttachmentsAction,
receiveAccessionAttachmentSuccessAction,
showDialog,
}
type PropsFromRedux = ReturnType<typeof mapStateToProps> & typeof mapDispatch;
......
......@@ -13,12 +13,12 @@ import { closeDialog } from "@gringlobal-ce/client/action/dialog";
interface IPrintReportDialogProps extends React.ClassAttributes<any>, WithTranslation, WithWidth {
showSnackbar: (message: string) => void;
entityPath: string;
selectedItemIds: number[]
dialogKey: string;
dialogKeyOpened: string;
closeDialog: () => void;
modelName: string;
endpoint: string;
}
class PrintReportDialog extends React.Component<IPrintReportDialogProps, any> {
......@@ -36,29 +36,27 @@ class PrintReportDialog extends React.Component<IPrintReportDialogProps, any> {
}
private checkMultiple = () => {
const { entityPath, modelName, t } = this.props;
const { endpoint, modelName, t } = this.props;
ReportService.getReportList(entityPath).then((reports) => {
if (reports.length > 1) {
ReportService.getReportList(endpoint).then((reports) => {
if (reports.length > 0) {
this.setState({ reports })
return;
if (reports.length === 1) {
// this.handlePrintReport({ report: reports[0] })
// return;
}
} else {
this.setState({ reports, error: t('public.c.generateReport.noReportsFound', { path: `/reports/${modelName}` }) })
}
if (reports.length === 1) {
this.handlePrintReport({ report: reports[0] })
return;
}
this.setState({ reports, error: t('public.c.generateReport.noReportsFound', { path: `/reports/${modelName}` }) })
}).catch((e) => {
this.setState({ error: e.data && e.data.error || e.toString() });
});
}
public handlePrintReport = ({ report }: { report: RepositoryFile }) => {
const { showSnackbar, entityPath, closeDialog, selectedItemIds, t } = this.props;
const { endpoint, showSnackbar, closeDialog, selectedItemIds, t } = this.props;
printPdfReport(entityPath, report.originalFilename, selectedItemIds).then((toPrint) => {
// this.setState({ androidIntent: `intent://print/#Intent;scheme=ggce;S.browser_fallback_url=${encodeURIComponent('https://gitlab.croptrust.org/grin-global/ggce-printer-android')};S.label=${encodeURIComponent(res)};end` });
// this.setState({ androidIntent: `intent://print/#Intent;scheme=ggce;S.label=${encodeURIComponent(toPrint)};end` });
printPdfReport(endpoint, report.originalFilename, selectedItemIds).then((pdfReport) => {
showSnackbar(t('public.c.generateReport.reportReady'));
closeDialog()
})
......@@ -70,7 +68,7 @@ class PrintReportDialog extends React.Component<IPrintReportDialogProps, any> {
}
public render() {
const { t, width, modelName, entityPath, dialogKeyOpened, closeDialog } = this.props;
const { t, width, endpoint, modelName, dialogKeyOpened, closeDialog } = this.props;
const { error, reports } = this.state;
return (
......@@ -86,8 +84,8 @@ class PrintReportDialog extends React.Component<IPrintReportDialogProps, any> {
error={ error }
resetError={ this.resetError }
reportsLoaded={ reports }
endpoint={ endpoint }
modelName={ modelName }
entityPath={ entityPath }
/>
);
}
......
......@@ -12,8 +12,8 @@ import { showSnackbar } from "@gringlobal-ce/client/action/snackbar";
import RepositoryFile from "@gringlobal-ce/client/model/repository/RepositoryFile";
import Loading from "@gringlobal-ce/client/ui/common/Loading";
const PrintReportForm = ({ t, onSubmit, initialValues, entityPath, reportsLoaded, showSnackbar, modelName, error }:
{ showSnackbar: (msg) => void, onSubmit: (values) => void, entityPath: string, modelName: string, reportsLoaded: RepositoryFile[] } & FormProps & WithTranslation) => {
const PrintReportForm = ({ t, onSubmit, initialValues, endpoint, reportsLoaded, showSnackbar, modelName, error }:
{ showSnackbar: (msg) => void, onSubmit: (values) => void, endpoint: string, modelName: string, reportsLoaded: RepositoryFile[] } & FormProps & WithTranslation) => {
const [ selectedReportId, setSelectedReportId ] = React.useState(null)
const [ internalError, setInternalError ] = React.useState(null)
const [ reports, setReports ] = React.useState(reportsLoaded)
......@@ -22,8 +22,8 @@ const PrintReportForm = ({ t, onSubmit, initialValues, entityPath, reportsLoaded
if (reportsLoaded && !reports) {
setReports(reportsLoaded)
}
if (!reports && !reportsLoaded && entityPath) {
ReportService.getReportList(entityPath).then((data) => {
if (!reports && !reportsLoaded && endpoint) {
ReportService.getReportList(endpoint).then((data) => {
if (data.length === 0) {
setInternalError(t('public.c.generateReport.noReportsFound', { path: `/reports/${modelName}` }))
}
......@@ -33,7 +33,7 @@ const PrintReportForm = ({ t, onSubmit, initialValues, entityPath, reportsLoaded
showSnackbar(t('common:label.errorHappened'));
});
}
}, [entityPath, reportsLoaded])
}, [reports, endpoint, reportsLoaded])
const handleSubmit = () => {
if (!reports || reports?.length === 0) {
......
......@@ -40,7 +40,6 @@ export const CropTableDefaultConfig = {
const CropTableConfig = new TableConfiguration(CropTableDefaultConfig);
const printDialogKey = "crop-print-pdf-dialog"
class BrowsePage extends React.Component<PropsFromRedux & WithBrowsePage & WithTranslation> {
public state = {
......@@ -52,6 +51,8 @@ class BrowsePage extends React.Component<PropsFromRedux & WithBrowsePage & WithT
({}) => listCropsAction(),
];
private static PRINT_DIALOG_KEY = 'crop-print-pdf-dialog';
public constructor(props) {
super(props);
}
......@@ -83,7 +84,7 @@ class BrowsePage extends React.Component<PropsFromRedux & WithBrowsePage & WithT
const actions = [
{
title: 'common:action.generatePdf',
action: () => showDialog(printDialogKey),
action: () => showDialog(BrowsePage.PRINT_DIALOG_KEY),
icon: <PrintIcon/>,
},
]
......@@ -125,10 +126,10 @@ class BrowsePage extends React.Component<PropsFromRedux & WithBrowsePage & WithT
</>
</Authorize>
<PrintReportDialog
entityPath="/crop"
endpoint="crop"
modelName="Crop"
selectedItemIds={ selected }
dialogKey={ printDialogKey }
modelName={ t('client:model.name.Crop') }
dialogKey={ BrowsePage.PRINT_DIALOG_KEY }
/>
</>
);
......
......@@ -85,6 +85,8 @@ import RequestAttachForm from 'request/ui/c/RequestAttachForm';
import RequestItemActionDialog from 'request/ui/c/RequestItemActionDialog';
import CodeValueInfo from '@gringlobal-ce/client/model/gringlobal/CodeValueInfo';
import CreateCooperatorAction from 'cooperator/ui/c/CreateCooperatorAction';
import PrintReportDialog from 'common/PrintReportDialog';
import { showDialog } from '@gringlobal-ce/client/action/dialog';
const styles = (theme) => ({
tableWrapper: {
......@@ -153,6 +155,7 @@ class OrderRequestDetailsPage extends React.Component<PropsFromRedux & WithTrans
({ params: { id } }) => listOrderRequestItemsAction(id),
];
private static PRINT_DIALOG_KEY = 'order-print-pdf-dialog';
public static defaultTab = RequestTabs.REQUEST;
public state: {
......@@ -665,6 +668,9 @@ class OrderRequestDetailsPage extends React.Component<PropsFromRedux & WithTrans
</CardContent>
<CardActions>
<ButtonBar>
<Button variant="outlined" color="secondary" onClick={ () => this.props.showDialog(OrderRequestDetailsPage.PRINT_DIALOG_KEY) }>
{ t('common:action.generatePdf') }
</Button>
<Button variant="contained" color="primary" onClick={ this.printRequest } startIcon={ <PrintIcon /> }>
{ t('request.public.p.retrievalList.label.printRequest') }
</Button>
......@@ -749,6 +755,12 @@ class OrderRequestDetailsPage extends React.Component<PropsFromRedux & WithTrans
size="lg"
initialValues={ selectedItem || {} }
/>
<PrintReportDialog
endpoint="order"
modelName="OrderRequest"
selectedItemIds={ [ request?.id ] }
dialogKey={ OrderRequestDetailsPage.PRINT_DIALOG_KEY }
/>
</TabPanel>
<TabPanel value={ currentTab } index={ RequestTabs.ATTACHMENTS }>
<Card>
......@@ -824,6 +836,7 @@ const mapDispatch = {
editOrderRequestItemAction,
navigateTo,
showSnackbar,
showDialog,
}
type PropsFromRedux = ReturnType<typeof mapStateToProps> & typeof mapDispatch;
......
......@@ -66,7 +66,7 @@ const withDialog = (Component: React.ComponentType<any>) => {
<Component initialValues={ initialValues ?? {} } error={ error } { ...formExtraProps } />
</DialogContent>
<DialogActions>
<Button type="submit" form={ formId } variant="contained" color="primary">{ submitButtonLabel || t('common:action.save') }</Button>
<Button type="submit" disabled={ !!error } form={ formId } variant="contained" color="primary">{ submitButtonLabel || t('common:action.save') }</Button>
{ ...additionalActions }
<Button variant="text" color="primary" onClick={ onClose }>{ t('common:action.cancel') }</Button>
</DialogActions>
......
Supports Markdown
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