added ability to update the display name for perspective columns w/ burnettk

This commit is contained in:
jasquat 2022-12-01 14:46:09 -05:00
parent 98c775db8e
commit 3bf23f6624
6 changed files with 222 additions and 25 deletions

View File

@ -816,7 +816,6 @@ def process_instance_list(
) )
) )
# process_model_identifier = un_modify_modified_process_model_id(modified_process_model_identifier)
process_instance_query = ProcessInstanceModel.query process_instance_query = ProcessInstanceModel.query
# Always join that hot user table for good performance at serialization time. # Always join that hot user table for good performance at serialization time.
process_instance_query = process_instance_query.options( process_instance_query = process_instance_query.options(
@ -983,9 +982,8 @@ def process_instance_report_column_list() -> flask.wrappers.Response:
table_columns = ProcessInstanceReportService.builtin_column_options() table_columns = ProcessInstanceReportService.builtin_column_options()
columns_for_metadata = db.session.query(ProcessInstanceMetadataModel.key).distinct().all() # type: ignore columns_for_metadata = db.session.query(ProcessInstanceMetadataModel.key).distinct().all() # type: ignore
columns_for_metadata_strings = [ columns_for_metadata_strings = [
{"Header": i[0], "accessor": i[0]} for i in columns_for_metadata {"Header": i[0], "accessor": i[0], "filterable": True} for i in columns_for_metadata
] ]
# columns = sorted(table_columns + columns_for_metadata_strings)
return make_response(jsonify(table_columns + columns_for_metadata_strings), 200) return make_response(jsonify(table_columns + columns_for_metadata_strings), 200)
@ -1139,7 +1137,6 @@ def process_instance_report_show(
page: int = 1, page: int = 1,
per_page: int = 100, per_page: int = 100,
) -> flask.wrappers.Response: ) -> flask.wrappers.Response:
"""Process_instance_list."""
process_instances = ProcessInstanceModel.query.order_by( # .filter_by(process_model_identifier=process_model.id) process_instances = ProcessInstanceModel.query.order_by( # .filter_by(process_model_identifier=process_model.id)
ProcessInstanceModel.start_in_seconds.desc(), ProcessInstanceModel.id.desc() # type: ignore ProcessInstanceModel.start_in_seconds.desc(), ProcessInstanceModel.id.desc() # type: ignore
).paginate( ).paginate(

View File

@ -243,13 +243,13 @@ class ProcessInstanceReportService:
def builtin_column_options(cls) -> list[dict]: def builtin_column_options(cls) -> list[dict]:
"""Builtin_column_options.""" """Builtin_column_options."""
return [ return [
{"Header": "Id", "accessor": "id"}, {"Header": "Id", "accessor": "id", "filterable": False},
{ {
"Header": "Process", "Header": "Process",
"accessor": "process_model_display_name", "accessor": "process_model_display_name", "filterable": False,
}, },
{"Header": "Start", "accessor": "start_in_seconds"}, {"Header": "Start", "accessor": "start_in_seconds", "filterable": False},
{"Header": "End", "accessor": "end_in_seconds"}, {"Header": "End", "accessor": "end_in_seconds", "filterable": False},
{"Header": "Username", "accessor": "username"}, {"Header": "Username", "accessor": "username", "filterable": False},
{"Header": "Status", "accessor": "status"}, {"Header": "Status", "accessor": "status", "filterable": False},
] ]

View File

@ -50,7 +50,6 @@ export default function ProcessInstanceListSaveAsReport({
event.preventDefault(); event.preventDefault();
const orderByArray = orderBy.split(',').filter((n) => n); const orderByArray = orderBy.split(',').filter((n) => n);
const filterByArray: any = []; const filterByArray: any = [];
if (processModelSelection) { if (processModelSelection) {
@ -122,7 +121,7 @@ export default function ProcessInstanceListSaveAsReport({
value={identifier} value={identifier}
onChange={(e: any) => setIdentifier(e.target.value)} onChange={(e: any) => setIdentifier(e.target.value)}
/> />
<Button disabled={!hasIdentifier()} size="sm"> <Button disabled={!hasIdentifier()} size="sm" type="submit">
{buttonText} {buttonText}
</Button> </Button>
</Stack> </Stack>

View File

@ -7,7 +7,7 @@ import {
} from 'react-router-dom'; } from 'react-router-dom';
// @ts-ignore // @ts-ignore
import { Filter, Close } from '@carbon/icons-react'; import { Filter, Close, AddAlt, AddFilled } from '@carbon/icons-react';
import { import {
Button, Button,
ButtonSet, ButtonSet,
@ -22,6 +22,11 @@ import {
TableRow, TableRow,
TimePicker, TimePicker,
Tag, Tag,
InlineNotification,
Stack,
Modal,
ComboBox,
TextInput,
// @ts-ignore // @ts-ignore
} from '@carbon/react'; } from '@carbon/react';
import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config'; import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config';
@ -88,7 +93,7 @@ export default function ProcessInstanceListTable({
autoReload = false, autoReload = false,
}: OwnProps) { }: OwnProps) {
const params = useParams(); const params = useParams();
const [searchParams] = useSearchParams(); const [searchParams, setSearchParams] = useSearchParams();
const navigate = useNavigate(); const navigate = useNavigate();
const [processInstances, setProcessInstances] = useState([]); const [processInstances, setProcessInstances] = useState([]);
@ -132,6 +137,12 @@ export default function ProcessInstanceListTable({
const [availableReportColumns, setAvailableReportColumns] = useState< const [availableReportColumns, setAvailableReportColumns] = useState<
ReportColumn[] ReportColumn[]
>([]); >([]);
const [processInstanceReportJustSaved, setProcessInstanceReportJustSaved] =
useState<boolean>(false);
const [showColumnForm, setShowColumnForm] = useState<boolean>(false);
const [reportColumnToOperateOn, setReportColumnToOperateOn] =
useState<ReportColumn | null>(null);
const [columnFormMode, setColumnFormMode] = useState<string>('');
const dateParametersToAlwaysFilterBy: dateParameters = useMemo(() => { const dateParametersToAlwaysFilterBy: dateParameters = useMemo(() => {
return { return {
@ -357,6 +368,23 @@ export default function ProcessInstanceListTable({
processModelAvailableItems, processModelAvailableItems,
]); ]);
const processInstanceReportSaveTag = () => {
if (processInstanceReportJustSaved) {
return (
<InlineNotification
title="Perspective Saved"
subtitle={`as '${
processInstanceReportSelection
? processInstanceReportSelection.display_name
: ''
}'`}
kind="success"
/>
);
}
return null;
};
// does the comparison, but also returns false if either argument // does the comparison, but also returns false if either argument
// is not truthy and therefore not comparable. // is not truthy and therefore not comparable.
const isTrueComparison = (param1: any, operation: any, param2: any) => { const isTrueComparison = (param1: any, operation: any, param2: any) => {
@ -473,6 +501,7 @@ export default function ProcessInstanceListTable({
} }
setErrorMessage(null); setErrorMessage(null);
setProcessInstanceReportJustSaved(false);
navigate(`/admin/process-instances?${queryParamString}`); navigate(`/admin/process-instances?${queryParamString}`);
}; };
@ -560,17 +589,22 @@ export default function ProcessInstanceListTable({
setEndToTime(''); setEndToTime('');
}; };
const processInstanceReportDidChange = (selection: any) => { const processInstanceReportDidChange = (
selection: any,
savedReport: boolean = false
) => {
clearFilters(); clearFilters();
const selectedReport = selection.selectedItem; const selectedReport = selection.selectedItem;
setProcessInstanceReportSelection(selectedReport); setProcessInstanceReportSelection(selectedReport);
const queryParamString = selectedReport let queryParamString = '';
? `&report_identifier=${selectedReport.id}` if (selectedReport) {
: ''; queryParamString = `&report_identifier=${selectedReport.id}`;
}
setErrorMessage(null); setErrorMessage(null);
setProcessInstanceReportJustSaved(savedReport);
navigate(`/admin/process-instances?${queryParamString}`); navigate(`/admin/process-instances?${queryParamString}`);
}; };
@ -578,12 +612,21 @@ export default function ProcessInstanceListTable({
return (reportMetadata as any).columns; return (reportMetadata as any).columns;
}; };
const reportColumnAccessors = () => {
return reportColumns().map((reportColumn: ReportColumn) => {
return reportColumn.accessor;
});
};
const saveAsReportComponent = () => { const saveAsReportComponent = () => {
// TODO onSuccess reload/select the new report in the report search // TODO onSuccess reload/select the new report in the report search
const callback = (identifier: string) => { const callback = (identifier: string) => {
processInstanceReportDidChange({ processInstanceReportDidChange(
{
selectedItem: { id: identifier, display_name: identifier }, selectedItem: { id: identifier, display_name: identifier },
}); },
true
);
}; };
const { const {
valid, valid,
@ -611,18 +654,146 @@ export default function ProcessInstanceListTable({
); );
}; };
const removeColumn = (reportColumn: ReportColumn) => {
const reportMetadataCopy = { ...reportMetadata };
const newColumns = reportColumns().filter(
(rc: ReportColumn) => rc.accessor !== reportColumn.accessor
);
Object.assign(reportMetadataCopy, { columns: newColumns });
setReportMetadata(reportMetadataCopy);
};
const handleColumnFormClose = () => {
setShowColumnForm(false);
setColumnFormMode('');
setReportColumnToOperateOn(null);
};
const handleUpdateColumn = () => {
if (reportColumnToOperateOn) {
const reportMetadataCopy = { ...reportMetadata };
let newReportColumns = null;
if (columnFormMode === 'new') {
newReportColumns = reportColumns().concat([reportColumnToOperateOn]);
} else {
newReportColumns = reportColumns().map((rc: ReportColumn) => {
if (rc.accessor === reportColumnToOperateOn.accessor) {
return reportColumnToOperateOn;
}
return rc;
});
}
Object.assign(reportMetadataCopy, {
columns: newReportColumns,
});
setReportMetadata(reportMetadataCopy);
setReportColumnToOperateOn(null);
setShowColumnForm(false);
setShowColumnForm(false);
}
};
const updateReportColumn = (event: any) => {
setReportColumnToOperateOn(event.selectedItem);
};
// options includes item and inputValue
const shouldFilterReportColumn = (options: any) => {
const reportColumn: ReportColumn = options.item;
const { inputValue } = options;
return (
!reportColumnAccessors().includes(reportColumn.accessor) &&
(reportColumn.accessor || '')
.toLowerCase()
.includes((inputValue || '').toLowerCase())
);
};
const columnForm = () => {
if (columnFormMode === '') {
return null;
}
const formElements = [
<TextInput
id="report-column-display-name"
name="report-column-display-name"
labelText="Display Name"
disabled={!reportColumnToOperateOn}
value={reportColumnToOperateOn ? reportColumnToOperateOn.Header : ''}
onChange={(event: any) => {
if (reportColumnToOperateOn) {
const reportColumnToOperateOnCopy = {
...reportColumnToOperateOn,
};
reportColumnToOperateOnCopy.Header = event.target.value;
setReportColumnToOperateOn(reportColumnToOperateOnCopy);
}
}}
/>,
];
if (columnFormMode === 'new') {
formElements.push(
<ComboBox
onChange={updateReportColumn}
className="combo-box-in-modal"
id="report-column-selection"
data-qa="report-column-selection"
data-modal-primary-focus
items={availableReportColumns}
itemToString={(reportColumn: ReportColumn) => {
if (reportColumn) {
return reportColumn.accessor;
}
return null;
}}
shouldFilterItem={shouldFilterReportColumn}
placeholder="Choose a report column"
titleText="Report Column"
/>
);
}
const modalHeading =
columnFormMode === 'new'
? 'Add Column'
: `Edit ${
reportColumnToOperateOn ? reportColumnToOperateOn.accessor : ''
} column`;
return (
<Modal
open={showColumnForm}
modalHeading={modalHeading}
primaryButtonText="Save"
primaryButtonDisabled={!reportColumnToOperateOn}
onRequestSubmit={handleUpdateColumn}
onRequestClose={handleColumnFormClose}
hasScrollingContent
>
{formElements}
</Modal>
);
};
const columnSelections = () => { const columnSelections = () => {
if (reportColumns()) { if (reportColumns()) {
const tags: any = []; const tags: any = [];
(reportColumns() as any).forEach((reportColumn: ReportColumn) => { (reportColumns() as any).forEach((reportColumn: ReportColumn) => {
let tagType = 'cool-gray';
if (reportColumn.filterable) {
tagType = 'green';
}
tags.push( tags.push(
<Tag type="cool-gray" size="sm" title={reportColumn.accessor}> <Tag type={tagType} size="sm" title={reportColumn.accessor}>
<Button <Button
kind="ghost" kind="ghost"
size="sm" size="sm"
className="button-tag-icon" className="button-tag-icon"
title="Edit Header" title="Edit Header"
onClick={() => {
setReportColumnToOperateOn(reportColumn);
setShowColumnForm(true);
setColumnFormMode('edit');
}}
> >
{reportColumn.Header} {reportColumn.Header}
</Button> </Button>
@ -634,12 +805,29 @@ export default function ProcessInstanceListTable({
hasIconOnly hasIconOnly
size="sm" size="sm"
kind="ghost" kind="ghost"
onClick={toggleShowFilterOptions} onClick={() => removeColumn(reportColumn)}
/> />
</Tag> </Tag>
); );
}); });
return tags; return (
<Stack orientation="horizontal">
{tags}
<Button
data-qa="add-column-button"
renderIcon={AddAlt}
iconDescription="Filter Options"
className="with-tiny-top-margin"
kind="ghost"
hasIconOnly
size="sm"
onClick={() => {
setShowColumnForm(true);
setColumnFormMode('new');
}}
/>
</Stack>
);
} }
return null; return null;
}; };
@ -651,7 +839,9 @@ export default function ProcessInstanceListTable({
return ( return (
<> <>
<Grid fullWidth className="with-bottom-margin"> <Grid fullWidth className="with-bottom-margin">
<Column md={8}>{columnSelections()}</Column> <Column md={8} lg={16} sm={4}>
{columnSelections()}
</Column>
</Grid> </Grid>
<Grid fullWidth className="with-bottom-margin"> <Grid fullWidth className="with-bottom-margin">
<Column md={8}> <Column md={8}>
@ -903,6 +1093,8 @@ export default function ProcessInstanceListTable({
} }
return ( return (
<> <>
{columnForm()}
{processInstanceReportSaveTag()}
{filterComponent()} {filterComponent()}
{reportSearchComponent()} {reportSearchComponent()}
<PaginationForTable <PaginationForTable

View File

@ -169,6 +169,10 @@ h1.with-icons {
margin-top: 1em; margin-top: 1em;
} }
.with-tiny-top-margin {
margin-top: 4px;
}
.with-large-bottom-margin { .with-large-bottom-margin {
margin-bottom: 3em; margin-bottom: 3em;
} }
@ -334,3 +338,7 @@ td.actions-cell {
.no-wrap .cds--label--inline{ .no-wrap .cds--label--inline{
word-break: normal; word-break: normal;
} }
.combo-box-in-modal {
height: 300px;
}

View File

@ -156,4 +156,5 @@ export interface FormField {
export interface ReportColumn { export interface ReportColumn {
Header: string; Header: string;
accessor: string; accessor: string;
filterable: boolean;
} }