Allow switching between user defined reports (#56)

This commit is contained in:
jbirddog 2022-11-22 09:35:42 -05:00 committed by GitHub
parent 2f1329e046
commit 016d2b19e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 149 additions and 34 deletions

View File

@ -71,37 +71,6 @@ class ProcessInstanceReportModel(SpiffworkflowBaseDBModel):
created_at_in_seconds = db.Column(db.Integer) created_at_in_seconds = db.Column(db.Integer)
updated_at_in_seconds = db.Column(db.Integer) updated_at_in_seconds = db.Column(db.Integer)
@classmethod
def default_report(cls, user: UserModel) -> ProcessInstanceReportModel:
"""Default_report."""
identifier = "default"
process_instance_report = ProcessInstanceReportModel.query.filter_by(
identifier=identifier, created_by_id=user.id
).first()
# TODO replace with system report that is loaded on launch (or similar)
if process_instance_report is None:
report_metadata = {
"columns": [
{"Header": "id", "accessor": "id"},
{
"Header": "process_model_identifier",
"accessor": "process_model_identifier",
},
{"Header": "start_in_seconds", "accessor": "start_in_seconds"},
{"Header": "end_in_seconds", "accessor": "end_in_seconds"},
{"Header": "status", "accessor": "status"},
],
}
process_instance_report = cls(
identifier=identifier,
created_by_id=user.id,
report_metadata=report_metadata,
)
return process_instance_report # type: ignore
@classmethod @classmethod
def add_fixtures(cls) -> None: def add_fixtures(cls) -> None:
"""Add_fixtures.""" """Add_fixtures."""

View File

@ -846,6 +846,7 @@ def process_instance_list(
report_metadata = process_instance_report.report_metadata report_metadata = process_instance_report.report_metadata
response_json = { response_json = {
"report_identifier": process_instance_report.identifier,
"report_metadata": report_metadata, "report_metadata": report_metadata,
"results": results, "results": results,
"filters": report_filter.to_dict(), "filters": report_filter.to_dict(),

View File

@ -48,10 +48,29 @@ class ProcessInstanceReportService:
) -> ProcessInstanceReportModel: ) -> ProcessInstanceReportModel:
"""Report_with_filter.""" """Report_with_filter."""
if report_identifier is None: if report_identifier is None:
return ProcessInstanceReportModel.default_report(user) report_identifier = "default"
process_instance_report = ProcessInstanceReportModel.query.filter_by(
identifier=report_identifier, created_by_id=user.id
).first()
if process_instance_report is not None:
return process_instance_report # type: ignore
# TODO replace with system reports that are loaded on launch (or similar) # TODO replace with system reports that are loaded on launch (or similar)
temp_system_metadata_map = { temp_system_metadata_map = {
"default": {
"columns": [
{"Header": "id", "accessor": "id"},
{
"Header": "process_model_identifier",
"accessor": "process_model_identifier",
},
{"Header": "start_in_seconds", "accessor": "start_in_seconds"},
{"Header": "end_in_seconds", "accessor": "end_in_seconds"},
{"Header": "status", "accessor": "status"},
],
},
"system_report_instances_initiated_by_me": { "system_report_instances_initiated_by_me": {
"columns": [ "columns": [
{ {
@ -96,7 +115,7 @@ class ProcessInstanceReportService:
report_metadata=temp_system_metadata_map[report_identifier], report_metadata=temp_system_metadata_map[report_identifier],
) )
return process_instance_report return process_instance_report # type: ignore
@classmethod @classmethod
def filter_by_to_dict( def filter_by_to_dict(

View File

@ -43,8 +43,13 @@ import HttpService from '../services/HttpService';
import 'react-bootstrap-typeahead/css/Typeahead.css'; import 'react-bootstrap-typeahead/css/Typeahead.css';
import 'react-bootstrap-typeahead/css/Typeahead.bs5.css'; import 'react-bootstrap-typeahead/css/Typeahead.bs5.css';
import { PaginationObject, ProcessModel } from '../interfaces'; import {
PaginationObject,
ProcessModel,
ProcessInstanceReport,
} from '../interfaces';
import ProcessModelSearch from './ProcessModelSearch'; import ProcessModelSearch from './ProcessModelSearch';
import ProcessInstanceReportSearch from './ProcessInstanceReportSearch';
type OwnProps = { type OwnProps = {
filtersEnabled?: boolean; filtersEnabled?: boolean;
@ -102,6 +107,8 @@ export default function ProcessInstanceListTable({
>([]); >([]);
const [processModelSelection, setProcessModelSelection] = const [processModelSelection, setProcessModelSelection] =
useState<ProcessModel | null>(null); useState<ProcessModel | null>(null);
const [processInstanceReportSelection, setProcessInstanceReportSelection] =
useState<ProcessInstanceReport | null>(null);
const dateParametersToAlwaysFilterBy: dateParameters = useMemo(() => { const dateParametersToAlwaysFilterBy: dateParameters = useMemo(() => {
return { return {
@ -136,6 +143,14 @@ export default function ProcessInstanceListTable({
setReportMetadata(result.report_metadata); setReportMetadata(result.report_metadata);
setPagination(result.pagination); setPagination(result.pagination);
setProcessInstanceFilters(result.filters); setProcessInstanceFilters(result.filters);
// TODO: need to iron out this interaction some more
if (result.report_identifier !== 'default') {
setProcessInstanceReportSelection({
id: result.report_identifier,
display_name: result.report_identifier,
});
}
} }
function getProcessInstances() { function getProcessInstances() {
// eslint-disable-next-line prefer-const // eslint-disable-next-line prefer-const
@ -156,6 +171,11 @@ export default function ProcessInstanceListTable({
queryParamString += `&user_filter=${userAppliedFilter}`; queryParamString += `&user_filter=${userAppliedFilter}`;
} }
const reportIdentifier = searchParams.get('report_identifier');
if (reportIdentifier) {
queryParamString += `&report_identifier=${reportIdentifier}`;
}
Object.keys(dateParametersToAlwaysFilterBy).forEach( Object.keys(dateParametersToAlwaysFilterBy).forEach(
(paramName: string) => { (paramName: string) => {
const dateFunctionToCall = const dateFunctionToCall =
@ -389,6 +409,10 @@ export default function ProcessInstanceListTable({
queryParamString += `&process_model_identifier=${processModelSelection.id}`; queryParamString += `&process_model_identifier=${processModelSelection.id}`;
} }
if (processInstanceReportSelection) {
queryParamString += `&report_identifier=${processInstanceReportSelection.id}`;
}
setErrorMessage(null); setErrorMessage(null);
navigate(`/admin/process-instances?${queryParamString}`); navigate(`/admin/process-instances?${queryParamString}`);
}; };
@ -664,6 +688,29 @@ export default function ProcessInstanceListTable({
setShowFilterOptions(!showFilterOptions); setShowFilterOptions(!showFilterOptions);
}; };
const processInstanceReportDidChange = (selection: any) => {
clearFilters();
const selectedReport = selection.selectedItem;
setProcessInstanceReportSelection(selectedReport);
const queryParamString = selectedReport
? `&report_identifier=${selectedReport.id}`
: '';
setErrorMessage(null);
navigate(`/admin/process-instances?${queryParamString}`);
};
const reportSearchComponent = () => {
return (
<ProcessInstanceReportSearch
onChange={processInstanceReportDidChange}
selectedItem={processInstanceReportSelection}
/>
);
};
const filterComponent = () => { const filterComponent = () => {
if (!filtersEnabled) { if (!filtersEnabled) {
return null; return null;
@ -707,6 +754,7 @@ export default function ProcessInstanceListTable({
return ( return (
<> <>
{filterComponent()} {filterComponent()}
{reportSearchComponent()}
<PaginationForTable <PaginationForTable
page={page} page={page}
perPage={perPage} perPage={perPage}

View File

@ -0,0 +1,73 @@
import { useState } from 'react';
import {
ComboBox,
// @ts-ignore
} from '@carbon/react';
import { truncateString } from '../helpers';
import { ProcessInstanceReport } from '../interfaces';
import HttpService from '../services/HttpService';
type OwnProps = {
onChange: (..._args: any[]) => any;
selectedItem?: ProcessInstanceReport | null;
titleText?: string;
};
export default function ProcessInstanceReportSearch({
selectedItem,
onChange,
titleText = 'Process instance perspectives',
}: OwnProps) {
const [processInstanceReports, setProcessInstanceReports] = useState<
ProcessInstanceReport[] | null
>(null);
function setProcessInstanceReportsFromResult(result: any) {
const processInstanceReportsFromApi = result.map((item: any) => {
return { id: item.identifier, display_name: item.identifier };
});
setProcessInstanceReports(processInstanceReportsFromApi);
}
if (processInstanceReports === null) {
setProcessInstanceReports([]);
HttpService.makeCallToBackend({
path: `/process-instances/reports`,
successCallback: setProcessInstanceReportsFromResult,
});
}
const shouldFilterProcessInstanceReport = (options: any) => {
const processInstanceReport: ProcessInstanceReport = options.item;
const { inputValue } = options;
return `${processInstanceReport.id} (${processInstanceReport.display_name})`.includes(
inputValue
);
};
const reportsAvailable = () => {
return processInstanceReports && processInstanceReports.length > 0;
};
return reportsAvailable() ? (
<ComboBox
onChange={onChange}
id="process-instance-report-select"
data-qa="process-instance-report-selection"
items={processInstanceReports}
itemToString={(processInstanceReport: ProcessInstanceReport) => {
if (processInstanceReport) {
return `${processInstanceReport.id} (${truncateString(
processInstanceReport.display_name,
20
)})`;
}
return null;
}}
shouldFilterItem={shouldFilterProcessInstanceReport}
placeholder="Choose a process instance perspective"
titleText={titleText}
selectedItem={selectedItem}
/>
) : null;
}

View File

@ -41,6 +41,11 @@ export interface ProcessInstance {
process_model_identifier: string; process_model_identifier: string;
} }
export interface ProcessInstanceReport {
id: string;
display_name: string;
}
export interface ProcessModel { export interface ProcessModel {
id: string; id: string;
description: string; description: string;