mirror of
https://github.com/sartography/spiff-arena.git
synced 2025-01-11 10:06:09 +00:00
reports seem to be working again w/ burnettk
This commit is contained in:
parent
e49734b9ef
commit
b86ddf8a96
34
spiffworkflow-backend/migrations/versions/68adb1d504e1_.py
Normal file
34
spiffworkflow-backend/migrations/versions/68adb1d504e1_.py
Normal file
@ -0,0 +1,34 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 68adb1d504e1
|
||||
Revises: 0c7428378d6e
|
||||
Create Date: 2023-04-27 12:24:01.771698
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '68adb1d504e1'
|
||||
down_revision = '0c7428378d6e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('process_instance_report', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('json_data_hash', sa.String(length=255), nullable=False))
|
||||
batch_op.create_index(batch_op.f('ix_process_instance_report_json_data_hash'), ['json_data_hash'], unique=False)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('process_instance_report', schema=None) as batch_op:
|
||||
batch_op.drop_index(batch_op.f('ix_process_instance_report_json_data_hash'))
|
||||
batch_op.drop_column('json_data_hash')
|
||||
|
||||
# ### end Alembic commands ###
|
@ -1298,17 +1298,29 @@ paths:
|
||||
items:
|
||||
$ref: "#/components/schemas/Workflow"
|
||||
|
||||
/process-instances/report-metadata/{report_hash}:
|
||||
/process-instances/report-metadata:
|
||||
parameters:
|
||||
- name: report_hash
|
||||
in: path
|
||||
required: true
|
||||
description: The unique id of an existing report
|
||||
in: query
|
||||
required: false
|
||||
description: The hash of a query that has been searched before.
|
||||
schema:
|
||||
type: string
|
||||
- name: report_id
|
||||
in: query
|
||||
required: false
|
||||
description: The unique id of an existing report.
|
||||
schema:
|
||||
type: integer
|
||||
- name: report_identifier
|
||||
in: query
|
||||
required: false
|
||||
description: Specifies the identifier of a report to use, if any
|
||||
schema:
|
||||
type: string
|
||||
get:
|
||||
operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_report_metadata_show
|
||||
summary: Returns the metadata associated with a given report hash.
|
||||
operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_report_show
|
||||
summary: Returns the metadata associated with a given report key. This favors report_hash over report_id and report_identifier.
|
||||
tags:
|
||||
- Process Instances
|
||||
responses:
|
||||
@ -1341,20 +1353,20 @@ paths:
|
||||
description: The page number to return. Defaults to page 1.
|
||||
schema:
|
||||
type: integer
|
||||
get:
|
||||
operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_report_show
|
||||
summary: Returns a report of process instances for a given process model
|
||||
tags:
|
||||
- Process Instances
|
||||
responses:
|
||||
"200":
|
||||
description: Workflow.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Workflow"
|
||||
# get:
|
||||
# operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_report_show
|
||||
# summary: Returns a report of process instances for a given process model
|
||||
# tags:
|
||||
# - Process Instances
|
||||
# responses:
|
||||
# "200":
|
||||
# description: Workflow.
|
||||
# content:
|
||||
# application/json:
|
||||
# schema:
|
||||
# type: array
|
||||
# items:
|
||||
# $ref: "#/components/schemas/Workflow"
|
||||
put:
|
||||
operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_report_update
|
||||
summary: Updates a process instance report
|
||||
|
@ -1,4 +1,10 @@
|
||||
from __future__ import annotations
|
||||
import json
|
||||
from hashlib import sha256
|
||||
from flask import current_app
|
||||
from typing import TypedDict
|
||||
from sqlalchemy.dialects.mysql import insert as mysql_insert
|
||||
from sqlalchemy.dialects.postgresql import insert as postgres_insert
|
||||
|
||||
from spiffworkflow_backend.models.db import db
|
||||
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
|
||||
@ -8,6 +14,11 @@ class JsonDataModelNotFoundError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class JsonDataDict(TypedDict):
|
||||
hash: str
|
||||
data: dict
|
||||
|
||||
|
||||
# delta algorithm <- just to save it for when we want to try to implement it:
|
||||
# a = {"hey": { "hey2": 2, "hey3": 3, "hey6": 7 }, "hey30": 3, "hey40": 4}
|
||||
# b = {"hey": { "hey2": 4, "hey5": 3 }, "hey20": 2, "hey30": 3}
|
||||
@ -42,3 +53,29 @@ class JsonDataModel(SpiffworkflowBaseDBModel):
|
||||
@classmethod
|
||||
def find_data_dict_by_hash(cls, hash: str) -> dict:
|
||||
return cls.find_object_by_hash(hash).data
|
||||
|
||||
@classmethod
|
||||
def insert_or_update_json_data_records(
|
||||
cls, json_data_hash_to_json_data_dict_mapping: dict[str, JsonDataDict]
|
||||
) -> None:
|
||||
list_of_dicts = [*json_data_hash_to_json_data_dict_mapping.values()]
|
||||
if len(list_of_dicts) > 0:
|
||||
on_duplicate_key_stmt = None
|
||||
if current_app.config["SPIFFWORKFLOW_BACKEND_DATABASE_TYPE"] == "mysql":
|
||||
insert_stmt = mysql_insert(JsonDataModel).values(list_of_dicts)
|
||||
on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update(data=insert_stmt.inserted.data)
|
||||
else:
|
||||
insert_stmt = postgres_insert(JsonDataModel).values(list_of_dicts)
|
||||
on_duplicate_key_stmt = insert_stmt.on_conflict_do_nothing(index_elements=["hash"])
|
||||
db.session.execute(on_duplicate_key_stmt)
|
||||
|
||||
@classmethod
|
||||
def insert_or_update_json_data_dict(cls, json_data_dict: JsonDataDict) -> None:
|
||||
cls.insert_or_update_json_data_records({json_data_dict["hash"]: json_data_dict})
|
||||
|
||||
@classmethod
|
||||
def create_and_insert_json_data_from_dict(cls, data: dict) -> str:
|
||||
json_data_hash = sha256(json.dumps(data, sort_keys=True).encode("utf8")).hexdigest()
|
||||
cls.insert_or_update_json_data_dict({'hash': json_data_hash, 'data': data})
|
||||
db.session.commit()
|
||||
return json_data_hash
|
||||
|
@ -1,5 +1,6 @@
|
||||
"""Process_instance."""
|
||||
from __future__ import annotations
|
||||
from spiffworkflow_backend.models.json_data import JsonDataModel # noqa: F401
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
@ -88,6 +89,11 @@ class ProcessInstanceReportModel(SpiffworkflowBaseDBModel):
|
||||
created_at_in_seconds = db.Column(db.Integer)
|
||||
updated_at_in_seconds = db.Column(db.Integer)
|
||||
|
||||
json_data_hash: str = db.Column(db.String(255), nullable=False, index=True)
|
||||
|
||||
def get_report_metadata(self) -> dict:
|
||||
return JsonDataModel.find_data_dict_by_hash(self.json_data_hash)
|
||||
|
||||
@classmethod
|
||||
def default_order_by(cls) -> list[str]:
|
||||
"""Default_order_by."""
|
||||
@ -154,10 +160,13 @@ class ProcessInstanceReportModel(SpiffworkflowBaseDBModel):
|
||||
f"Process instance report with identifier already exists: {identifier}"
|
||||
)
|
||||
|
||||
json_data_hash = JsonDataModel.create_and_insert_json_data_from_dict(report_metadata)
|
||||
|
||||
process_instance_report = cls(
|
||||
identifier=identifier,
|
||||
created_by_id=user.id,
|
||||
report_metadata=report_metadata,
|
||||
json_data_hash=json_data_hash,
|
||||
)
|
||||
db.session.add(process_instance_report)
|
||||
db.session.commit()
|
||||
|
@ -335,25 +335,42 @@ def process_instance_list(
|
||||
user=g.user,
|
||||
)
|
||||
|
||||
json_data_hash = sha256(json.dumps(body['report_metadata'], sort_keys=True).encode("utf8")).hexdigest()
|
||||
TaskService.insert_or_update_json_data_dict({'hash': json_data_hash, 'data': body['report_metadata']})
|
||||
db.session.commit()
|
||||
# json_data = JsonDataModel.query.filter_by(json_data_hash)
|
||||
json_data_hash = JsonDataModel.create_and_insert_json_data_from_dict(body['report_metadata'])
|
||||
response_json['report_hash'] = json_data_hash
|
||||
db.session.commit()
|
||||
|
||||
return make_response(jsonify(response_json), 200)
|
||||
|
||||
|
||||
def process_instance_report_metadata_show(
|
||||
report_hash: str,
|
||||
def process_instance_report_show(
|
||||
report_hash: Optional[str] = None,
|
||||
report_id: Optional[int] = None,
|
||||
report_identifier: Optional[str] = None,
|
||||
) -> flask.wrappers.Response:
|
||||
json_data = JsonDataModel.query.filter_by(hash=report_hash).first()
|
||||
if json_data is None:
|
||||
|
||||
if report_hash is None and report_id is None and report_identifier is None:
|
||||
raise ApiError(
|
||||
error_code="report_metadata_not_found",
|
||||
message=f"Could not find report metadata for {report_hash}.",
|
||||
error_code="report_key_missing",
|
||||
message="A report key is needed to lookup a report. Either choose a report_hash, report_id, or report_identifier.",
|
||||
)
|
||||
return make_response(jsonify(json_data.data), 200)
|
||||
response_result = {}
|
||||
if report_hash is not None:
|
||||
json_data = JsonDataModel.query.filter_by(hash=report_hash).first()
|
||||
if json_data is None:
|
||||
raise ApiError(
|
||||
error_code="report_metadata_not_found",
|
||||
message=f"Could not find report metadata for {report_hash}.",
|
||||
)
|
||||
response_result = {
|
||||
"id": 0,
|
||||
"identifier": "custom",
|
||||
"name": "custom",
|
||||
"report_metadata": json_data.data,
|
||||
}
|
||||
else:
|
||||
response_result = ProcessInstanceReportService.report_with_identifier(g.user, report_id, report_identifier)
|
||||
|
||||
return make_response(jsonify(response_result), 200)
|
||||
|
||||
|
||||
def process_instance_report_column_list(
|
||||
@ -490,39 +507,39 @@ def process_instance_report_delete(
|
||||
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
|
||||
|
||||
|
||||
def process_instance_report_show(
|
||||
report_id: int,
|
||||
page: int = 1,
|
||||
per_page: int = 100,
|
||||
) -> flask.wrappers.Response:
|
||||
"""Process_instance_report_show."""
|
||||
process_instances = ProcessInstanceModel.query.order_by(
|
||||
ProcessInstanceModel.start_in_seconds.desc(), ProcessInstanceModel.id.desc() # type: ignore
|
||||
).paginate(page=page, per_page=per_page, error_out=False)
|
||||
|
||||
process_instance_report = ProcessInstanceReportModel.query.filter_by(
|
||||
id=report_id,
|
||||
created_by_id=g.user.id,
|
||||
).first()
|
||||
if process_instance_report is None:
|
||||
raise ApiError(
|
||||
error_code="unknown_process_instance_report",
|
||||
message="Unknown process instance report",
|
||||
status_code=404,
|
||||
)
|
||||
|
||||
substitution_variables = request.args.to_dict()
|
||||
result_dict = process_instance_report.generate_report(process_instances.items, substitution_variables)
|
||||
|
||||
# update this if we go back to a database query instead of filtering in memory
|
||||
result_dict["pagination"] = {
|
||||
"count": len(result_dict["results"]),
|
||||
"total": len(result_dict["results"]),
|
||||
"pages": 1,
|
||||
}
|
||||
|
||||
return Response(json.dumps(result_dict), status=200, mimetype="application/json")
|
||||
|
||||
# def process_instance_report_show(
|
||||
# report_id: int,
|
||||
# page: int = 1,
|
||||
# per_page: int = 100,
|
||||
# ) -> flask.wrappers.Response:
|
||||
# """Process_instance_report_show."""
|
||||
# process_instances = ProcessInstanceModel.query.order_by(
|
||||
# ProcessInstanceModel.start_in_seconds.desc(), ProcessInstanceModel.id.desc() # type: ignore
|
||||
# ).paginate(page=page, per_page=per_page, error_out=False)
|
||||
#
|
||||
# process_instance_report = ProcessInstanceReportModel.query.filter_by(
|
||||
# id=report_id,
|
||||
# created_by_id=g.user.id,
|
||||
# ).first()
|
||||
# if process_instance_report is None:
|
||||
# raise ApiError(
|
||||
# error_code="unknown_process_instance_report",
|
||||
# message="Unknown process instance report",
|
||||
# status_code=404,
|
||||
# )
|
||||
#
|
||||
# substitution_variables = request.args.to_dict()
|
||||
# result_dict = process_instance_report.generate_report(process_instances.items, substitution_variables)
|
||||
#
|
||||
# # update this if we go back to a database query instead of filtering in memory
|
||||
# result_dict["pagination"] = {
|
||||
# "count": len(result_dict["results"]),
|
||||
# "total": len(result_dict["results"]),
|
||||
# "pages": 1,
|
||||
# }
|
||||
#
|
||||
# return Response(json.dumps(result_dict), status=200, mimetype="application/json")
|
||||
#
|
||||
|
||||
def process_instance_task_list_without_task_data_for_me(
|
||||
modified_process_model_identifier: str,
|
||||
|
@ -1,5 +1,6 @@
|
||||
"""APIs for dealing with process groups, process models, and process instances."""
|
||||
import json
|
||||
from spiffworkflow_backend.models.json_data import JsonDataModel # noqa: F401
|
||||
import os
|
||||
import uuid
|
||||
from sys import exc_info
|
||||
@ -220,7 +221,7 @@ def task_data_update(
|
||||
task_model, new_task_data_dict, "json_data_hash"
|
||||
)
|
||||
if json_data_dict is not None:
|
||||
TaskService.insert_or_update_json_data_records({json_data_dict["hash"]: json_data_dict})
|
||||
JsonDataModel.insert_or_update_json_data_records({json_data_dict["hash"]: json_data_dict})
|
||||
ProcessInstanceTmpService.add_event_to_process_instance(
|
||||
process_instance, ProcessInstanceEventType.task_data_edited.value, task_guid=task_guid
|
||||
)
|
||||
@ -537,7 +538,7 @@ def _task_submit_shared(
|
||||
task_model, spiff_task.data, "json_data_hash"
|
||||
)
|
||||
if json_data_dict is not None:
|
||||
TaskService.insert_or_update_json_data_dict(json_data_dict)
|
||||
JsonDataModel.insert_or_update_json_data_dict(json_data_dict)
|
||||
db.session.add(task_model)
|
||||
db.session.commit()
|
||||
else:
|
||||
|
@ -1737,7 +1737,7 @@ class ProcessInstanceProcessor:
|
||||
bpmn_definition_to_task_definitions_mappings=self.bpmn_definition_to_task_definitions_mappings,
|
||||
)
|
||||
task_service.update_task_model(task_model, spiff_task)
|
||||
TaskService.insert_or_update_json_data_records(task_service.json_data_dicts)
|
||||
JsonDataModel.insert_or_update_json_data_records(task_service.json_data_dicts)
|
||||
|
||||
ProcessInstanceTmpService.add_event_to_process_instance(
|
||||
self.process_instance_model,
|
||||
|
@ -15,14 +15,12 @@ from SpiffWorkflow.exceptions import WorkflowException # type: ignore
|
||||
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
||||
from SpiffWorkflow.task import TaskState
|
||||
from SpiffWorkflow.task import TaskStateNames
|
||||
from sqlalchemy.dialects.mysql import insert as mysql_insert
|
||||
from sqlalchemy.dialects.postgresql import insert as postgres_insert
|
||||
|
||||
from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel
|
||||
from spiffworkflow_backend.models.bpmn_process import BpmnProcessNotFoundError
|
||||
from spiffworkflow_backend.models.bpmn_process_definition import BpmnProcessDefinitionModel
|
||||
from spiffworkflow_backend.models.db import db
|
||||
from spiffworkflow_backend.models.json_data import JsonDataModel # noqa: F401
|
||||
from spiffworkflow_backend.models.json_data import JsonDataDict, JsonDataModel # noqa: F401
|
||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
||||
from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventModel
|
||||
from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventType
|
||||
@ -38,11 +36,6 @@ class StartAndEndTimes(TypedDict):
|
||||
end_in_seconds: Optional[float]
|
||||
|
||||
|
||||
class JsonDataDict(TypedDict):
|
||||
hash: str
|
||||
data: dict
|
||||
|
||||
|
||||
class TaskModelError(Exception):
|
||||
"""Copied from SpiffWorkflow.exceptions.WorkflowTaskException.
|
||||
|
||||
@ -130,7 +123,7 @@ class TaskService:
|
||||
db.session.bulk_save_objects(self.bpmn_processes.values())
|
||||
db.session.bulk_save_objects(self.task_models.values())
|
||||
db.session.bulk_save_objects(self.process_instance_events.values())
|
||||
self.__class__.insert_or_update_json_data_records(self.json_data_dicts)
|
||||
JsonDataModel.insert_or_update_json_data_records(self.json_data_dicts)
|
||||
|
||||
def process_parents_and_children_and_save_to_database(
|
||||
self,
|
||||
@ -483,10 +476,6 @@ class TaskService:
|
||||
bpmn_process.json_data_hash = bpmn_process_data_hash
|
||||
return json_data_dict
|
||||
|
||||
@classmethod
|
||||
def insert_or_update_json_data_dict(cls, json_data_dict: JsonDataDict) -> None:
|
||||
TaskService.insert_or_update_json_data_records({json_data_dict["hash"]: json_data_dict})
|
||||
|
||||
@classmethod
|
||||
def update_task_data_on_task_model_and_return_dict_if_updated(
|
||||
cls, task_model: TaskModel, task_data_dict: dict, task_model_data_column: str
|
||||
@ -610,21 +599,6 @@ class TaskService:
|
||||
new_properties_json["state"] = getattr(TaskState, state)
|
||||
task_model.properties_json = new_properties_json
|
||||
|
||||
@classmethod
|
||||
def insert_or_update_json_data_records(
|
||||
cls, json_data_hash_to_json_data_dict_mapping: dict[str, JsonDataDict]
|
||||
) -> None:
|
||||
list_of_dicts = [*json_data_hash_to_json_data_dict_mapping.values()]
|
||||
if len(list_of_dicts) > 0:
|
||||
on_duplicate_key_stmt = None
|
||||
if current_app.config["SPIFFWORKFLOW_BACKEND_DATABASE_TYPE"] == "mysql":
|
||||
insert_stmt = mysql_insert(JsonDataModel).values(list_of_dicts)
|
||||
on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update(data=insert_stmt.inserted.data)
|
||||
else:
|
||||
insert_stmt = postgres_insert(JsonDataModel).values(list_of_dicts)
|
||||
on_duplicate_key_stmt = insert_stmt.on_conflict_do_nothing(index_elements=["hash"])
|
||||
db.session.execute(on_duplicate_key_stmt)
|
||||
|
||||
@classmethod
|
||||
def get_extensions_from_task_model(cls, task_model: TaskModel) -> dict:
|
||||
task_definition = task_model.task_definition
|
||||
|
@ -75,73 +75,73 @@ export default function ProcessInstanceListSaveAsReport({
|
||||
const addProcessInstanceReport = (event: any) => {
|
||||
event.preventDefault();
|
||||
|
||||
// TODO: make a field to set this
|
||||
let orderByArray = ['-start_in_seconds', '-id'];
|
||||
if (orderBy) {
|
||||
orderByArray = orderBy.split(',').filter((n) => n);
|
||||
}
|
||||
const filterByArray: any = [];
|
||||
|
||||
if (processModelSelection) {
|
||||
filterByArray.push({
|
||||
field_name: 'process_model_identifier',
|
||||
field_value: processModelSelection.id,
|
||||
});
|
||||
}
|
||||
|
||||
if (processInitiatorSelection) {
|
||||
filterByArray.push({
|
||||
field_name: 'process_initiator_username',
|
||||
field_value: processInitiatorSelection.username,
|
||||
});
|
||||
}
|
||||
|
||||
if (processStatusSelection.length > 0) {
|
||||
filterByArray.push({
|
||||
field_name: 'process_status',
|
||||
field_value: processStatusSelection.join(','),
|
||||
operator: 'in',
|
||||
});
|
||||
}
|
||||
|
||||
if (startFromSeconds) {
|
||||
filterByArray.push({
|
||||
field_name: 'start_from',
|
||||
field_value: startFromSeconds,
|
||||
});
|
||||
}
|
||||
|
||||
if (startToSeconds) {
|
||||
filterByArray.push({
|
||||
field_name: 'start_to',
|
||||
field_value: startToSeconds,
|
||||
});
|
||||
}
|
||||
|
||||
if (endFromSeconds) {
|
||||
filterByArray.push({
|
||||
field_name: 'end_from',
|
||||
field_value: endFromSeconds,
|
||||
});
|
||||
}
|
||||
|
||||
if (endToSeconds) {
|
||||
filterByArray.push({
|
||||
field_name: 'end_to',
|
||||
field_value: endToSeconds,
|
||||
});
|
||||
}
|
||||
|
||||
reportMetadata.filter_by.forEach((reportFilter: ReportFilter) => {
|
||||
columnArray.forEach((reportColumn: ReportColumn) => {
|
||||
if (
|
||||
reportColumn.accessor === reportFilter.field_name &&
|
||||
reportColumn.filterable
|
||||
) {
|
||||
filterByArray.push(reportFilter);
|
||||
}
|
||||
});
|
||||
});
|
||||
// // TODO: make a field to set this
|
||||
// let orderByArray = ['-start_in_seconds', '-id'];
|
||||
// if (orderBy) {
|
||||
// orderByArray = orderBy.split(',').filter((n) => n);
|
||||
// }
|
||||
// const filterByArray: any = [];
|
||||
//
|
||||
// if (processModelSelection) {
|
||||
// filterByArray.push({
|
||||
// field_name: 'process_model_identifier',
|
||||
// field_value: processModelSelection.id,
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (processInitiatorSelection) {
|
||||
// filterByArray.push({
|
||||
// field_name: 'process_initiator_username',
|
||||
// field_value: processInitiatorSelection.username,
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (processStatusSelection.length > 0) {
|
||||
// filterByArray.push({
|
||||
// field_name: 'process_status',
|
||||
// field_value: processStatusSelection.join(','),
|
||||
// operator: 'in',
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (startFromSeconds) {
|
||||
// filterByArray.push({
|
||||
// field_name: 'start_from',
|
||||
// field_value: startFromSeconds,
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (startToSeconds) {
|
||||
// filterByArray.push({
|
||||
// field_name: 'start_to',
|
||||
// field_value: startToSeconds,
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (endFromSeconds) {
|
||||
// filterByArray.push({
|
||||
// field_name: 'end_from',
|
||||
// field_value: endFromSeconds,
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (endToSeconds) {
|
||||
// filterByArray.push({
|
||||
// field_name: 'end_to',
|
||||
// field_value: endToSeconds,
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// reportMetadata.filter_by.forEach((reportFilter: ReportFilter) => {
|
||||
// columnArray.forEach((reportColumn: ReportColumn) => {
|
||||
// if (
|
||||
// reportColumn.accessor === reportFilter.field_name &&
|
||||
// reportColumn.filterable
|
||||
// ) {
|
||||
// filterByArray.push(reportFilter);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
let path = `/process-instances/reports`;
|
||||
let httpMethod = 'POST';
|
||||
@ -156,11 +156,7 @@ export default function ProcessInstanceListSaveAsReport({
|
||||
httpMethod,
|
||||
postBody: {
|
||||
identifier,
|
||||
report_metadata: {
|
||||
columns: columnArray,
|
||||
order_by: orderByArray,
|
||||
filter_by: filterByArray,
|
||||
},
|
||||
report_metadata: reportMetadata,
|
||||
},
|
||||
});
|
||||
handleSaveFormClose();
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
||||
|
||||
// @ts-ignore
|
||||
@ -37,10 +37,7 @@ import {
|
||||
convertSecondsToFormattedDateString,
|
||||
convertSecondsToFormattedDateTime,
|
||||
convertSecondsToFormattedTimeHoursMinutes,
|
||||
decodeBase64,
|
||||
encodeBase64,
|
||||
getPageInfoFromSearchParams,
|
||||
getProcessModelFullIdentifierFromSearchParams,
|
||||
modifyProcessIdentifierForPathParam,
|
||||
refreshAtInterval,
|
||||
REFRESH_INTERVAL_SECONDS,
|
||||
@ -119,6 +116,7 @@ export default function ProcessInstanceListTable({
|
||||
if (variant === 'all') {
|
||||
processInstanceApiSearchPath = '/process-instances';
|
||||
}
|
||||
|
||||
const params = useParams();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const navigate = useNavigate();
|
||||
@ -282,9 +280,11 @@ export default function ProcessInstanceListTable({
|
||||
setProcessInstanceFilters(result.filters);
|
||||
|
||||
setReportMetadata(result.report.report_metadata);
|
||||
if (result.report.id) {
|
||||
setProcessInstanceReportSelection(result.report);
|
||||
}
|
||||
// if (processInstanceReport) {
|
||||
// if (processInstanceReport.id > 0) {
|
||||
// setProcessInstanceReportSelection(processInstanceReport);
|
||||
// }
|
||||
// }
|
||||
if (result.report_hash) {
|
||||
searchParams.set('report_hash', result.report_hash);
|
||||
setSearchParams(searchParams);
|
||||
@ -301,6 +301,11 @@ export default function ProcessInstanceListTable({
|
||||
}
|
||||
};
|
||||
|
||||
const getData = useCallback(() => {
|
||||
console.log('WE DO STUFF');
|
||||
stopRefreshing(null);
|
||||
}, []);
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
useEffect(() => {
|
||||
if (!permissionsLoaded) {
|
||||
@ -318,9 +323,6 @@ export default function ProcessInstanceListTable({
|
||||
setPagination(result.pagination);
|
||||
setProcessInstanceFilters(result.filters);
|
||||
setReportMetadata(result.report.report_metadata);
|
||||
if (result.report.id) {
|
||||
setProcessInstanceReportSelection(result.report);
|
||||
}
|
||||
}
|
||||
|
||||
// Useful to stop refreshing if an api call gets an error
|
||||
@ -334,18 +336,21 @@ export default function ProcessInstanceListTable({
|
||||
}
|
||||
};
|
||||
function getProcessInstances(
|
||||
reportMetadataBody: ReportMetadata | null = null
|
||||
processInstanceReport: ProcessInstanceReport | null = null
|
||||
) {
|
||||
if (listHasBeenFiltered) {
|
||||
return;
|
||||
}
|
||||
let reportMetadataBodyToUse = reportMetadataBody;
|
||||
if (!reportMetadataBodyToUse) {
|
||||
reportMetadataBodyToUse = {
|
||||
columns: [],
|
||||
filter_by: [],
|
||||
order_by: [],
|
||||
};
|
||||
let reportMetadataBodyToUse: ReportMetadata = {
|
||||
columns: [],
|
||||
filter_by: [],
|
||||
order_by: [],
|
||||
};
|
||||
if (processInstanceReport) {
|
||||
reportMetadataBodyToUse = processInstanceReport.report_metadata;
|
||||
if (processInstanceReport.id > 0) {
|
||||
setProcessInstanceReportSelection(processInstanceReport);
|
||||
}
|
||||
}
|
||||
|
||||
let selectedProcessModelIdentifier = processModelFullIdentifier;
|
||||
@ -391,22 +396,6 @@ export default function ProcessInstanceListTable({
|
||||
}
|
||||
);
|
||||
|
||||
// if (searchParams.get('report_id')) {
|
||||
// queryParamString += `&report_id=${searchParams.get('report_id')}`;
|
||||
// } else if (reportIdentifier) {
|
||||
// queryParamString += `&report_identifier=${reportIdentifier}`;
|
||||
// }
|
||||
|
||||
// if (searchParams.get('report_columns')) {
|
||||
// const reportColumnsBase64 = searchParams.get('report_columns');
|
||||
// if (reportColumnsBase64) {
|
||||
// const reportColumnsList = JSON.parse(
|
||||
// decodeBase64(reportColumnsBase64)
|
||||
// );
|
||||
// postBody.columns = reportColumnsList;
|
||||
// }
|
||||
// }
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
let { page, perPage } = getPageInfoFromSearchParams(
|
||||
searchParams,
|
||||
@ -427,8 +416,8 @@ export default function ProcessInstanceListTable({
|
||||
path: `${processInstanceApiSearchPath}?${queryParamString}`,
|
||||
successCallback: setProcessInstancesFromResult,
|
||||
httpMethod: 'POST',
|
||||
failureCallback: stopRefreshing,
|
||||
onUnauthorized: stopRefreshing,
|
||||
failureCallback: getData,
|
||||
onUnauthorized: getData,
|
||||
postBody: {
|
||||
report_metadata: reportMetadataBodyToUse,
|
||||
},
|
||||
@ -438,10 +427,19 @@ export default function ProcessInstanceListTable({
|
||||
if (listHasBeenFiltered) {
|
||||
return;
|
||||
}
|
||||
const reportHash = searchParams.get('report_hash');
|
||||
if (reportHash) {
|
||||
const queryParams: string[] = [];
|
||||
['report_hash', 'report_id', 'report_identifier'].forEach(
|
||||
(paramName: string) => {
|
||||
if (searchParams.get(paramName)) {
|
||||
queryParams.push(`${paramName}=${searchParams.get(paramName)}`);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (queryParams.length > 0) {
|
||||
const queryParamString = `?${queryParams.join('&')}`;
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/process-instances/report-metadata/${reportHash}`,
|
||||
path: `/process-instances/report-metadata${queryParamString}`,
|
||||
successCallback: getProcessInstances,
|
||||
});
|
||||
} else {
|
||||
@ -697,6 +695,85 @@ export default function ProcessInstanceListTable({
|
||||
}
|
||||
};
|
||||
|
||||
const getNewReportMetadataBasedOnPageWidgets = () => {
|
||||
const {
|
||||
valid,
|
||||
startFromSeconds,
|
||||
startToSeconds,
|
||||
endFromSeconds,
|
||||
endToSeconds,
|
||||
} = calculateStartAndEndSeconds();
|
||||
|
||||
if (!valid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let newReportMetadata = null;
|
||||
if (reportMetadata) {
|
||||
newReportMetadata = { ...reportMetadata };
|
||||
}
|
||||
if (!newReportMetadata) {
|
||||
newReportMetadata = {
|
||||
columns: [],
|
||||
filter_by: [],
|
||||
order_by: [],
|
||||
};
|
||||
}
|
||||
|
||||
addFieldValueToReportMetadata(
|
||||
newReportMetadata,
|
||||
'start_from',
|
||||
startFromSeconds
|
||||
);
|
||||
addFieldValueToReportMetadata(
|
||||
newReportMetadata,
|
||||
'start_to',
|
||||
startToSeconds
|
||||
);
|
||||
addFieldValueToReportMetadata(
|
||||
newReportMetadata,
|
||||
'end_from',
|
||||
endFromSeconds
|
||||
);
|
||||
addFieldValueToReportMetadata(newReportMetadata, 'end_to', endToSeconds);
|
||||
if (processStatusSelection.length > 0) {
|
||||
addFieldValueToReportMetadata(
|
||||
newReportMetadata,
|
||||
'process_status',
|
||||
processStatusSelection.join(',')
|
||||
);
|
||||
} else {
|
||||
removeFieldFromReportMetadata(newReportMetadata, 'process_status');
|
||||
}
|
||||
|
||||
if (processModelSelection) {
|
||||
addFieldValueToReportMetadata(
|
||||
newReportMetadata,
|
||||
'process_model_identifier',
|
||||
processModelSelection.id
|
||||
);
|
||||
} else {
|
||||
removeFieldFromReportMetadata(
|
||||
newReportMetadata,
|
||||
'process_model_identifier'
|
||||
);
|
||||
}
|
||||
|
||||
if (processInitiatorSelection) {
|
||||
addFieldValueToReportMetadata(
|
||||
newReportMetadata,
|
||||
'process_initiator_username',
|
||||
processInitiatorSelection.username
|
||||
);
|
||||
} else {
|
||||
removeFieldFromReportMetadata(
|
||||
newReportMetadata,
|
||||
'process_initiator_username'
|
||||
);
|
||||
}
|
||||
return newReportMetadata;
|
||||
};
|
||||
|
||||
const applyFilter = (event: any) => {
|
||||
event.preventDefault();
|
||||
setProcessInitiatorNotFoundErrorText('');
|
||||
@ -707,74 +784,10 @@ export default function ProcessInstanceListTable({
|
||||
undefined,
|
||||
paginationQueryParamPrefix
|
||||
);
|
||||
const {
|
||||
valid,
|
||||
startFromSeconds,
|
||||
startToSeconds,
|
||||
endFromSeconds,
|
||||
endToSeconds,
|
||||
} = calculateStartAndEndSeconds();
|
||||
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
let postBody = null;
|
||||
if (reportMetadata) {
|
||||
postBody = { ...reportMetadata };
|
||||
}
|
||||
if (!postBody) {
|
||||
postBody = {
|
||||
columns: [],
|
||||
filter_by: [],
|
||||
order_by: [],
|
||||
};
|
||||
}
|
||||
|
||||
addFieldValueToReportMetadata(postBody, 'start_from', startFromSeconds);
|
||||
addFieldValueToReportMetadata(postBody, 'start_to', startToSeconds);
|
||||
addFieldValueToReportMetadata(postBody, 'end_from', endFromSeconds);
|
||||
addFieldValueToReportMetadata(postBody, 'end_to', endToSeconds);
|
||||
if (processStatusSelection.length > 0) {
|
||||
addFieldValueToReportMetadata(
|
||||
postBody,
|
||||
'process_status',
|
||||
processStatusSelection.join(',')
|
||||
);
|
||||
} else {
|
||||
removeFieldFromReportMetadata(postBody, 'process_status');
|
||||
}
|
||||
|
||||
if (processModelSelection) {
|
||||
addFieldValueToReportMetadata(
|
||||
postBody,
|
||||
'process_model_identifier',
|
||||
processModelSelection.id
|
||||
);
|
||||
} else {
|
||||
removeFieldFromReportMetadata(postBody, 'process_model_identifier');
|
||||
}
|
||||
|
||||
// if (processInstanceReportSelection) {
|
||||
// addFieldValueToReportMetadata(
|
||||
// postBody,
|
||||
// 'report_id',
|
||||
// processInstanceReportSelection.id.toString()
|
||||
// );
|
||||
// }
|
||||
|
||||
if (processInitiatorSelection) {
|
||||
addFieldValueToReportMetadata(
|
||||
postBody,
|
||||
'process_initiator_username',
|
||||
processInitiatorSelection.username
|
||||
);
|
||||
} else {
|
||||
removeFieldFromReportMetadata(postBody, 'process_initiator_username');
|
||||
}
|
||||
|
||||
const newReportMetadata = getNewReportMetadataBasedOnPageWidgets();
|
||||
setListHasBeenFiltered(true);
|
||||
setReportMetadata(postBody);
|
||||
setReportMetadata(newReportMetadata);
|
||||
searchParams.set('per_page', perPage.toString());
|
||||
searchParams.set('page', page.toString());
|
||||
setSearchParams(searchParams);
|
||||
@ -783,7 +796,7 @@ export default function ProcessInstanceListTable({
|
||||
HttpService.makeCallToBackend({
|
||||
path: `${processInstanceApiSearchPath}?${queryParamString}`,
|
||||
httpMethod: 'POST',
|
||||
postBody: { report_metadata: postBody },
|
||||
postBody: { report_metadata: newReportMetadata },
|
||||
failureCallback: stopRefreshing,
|
||||
onUnauthorized: stopRefreshing,
|
||||
successCallback: (result: any) => {
|
||||
@ -907,14 +920,12 @@ export default function ProcessInstanceListTable({
|
||||
});
|
||||
};
|
||||
|
||||
// TODO onSuccess reload/select the new report in the report search
|
||||
const onSaveReportSuccess = (result: any, mode: string) => {
|
||||
processInstanceReportDidChange(
|
||||
{
|
||||
selectedItem: result,
|
||||
},
|
||||
mode
|
||||
);
|
||||
const onSaveReportSuccess = (
|
||||
processInstanceReport: ProcessInstanceReport
|
||||
) => {
|
||||
setProcessInstanceReportSelection(processInstanceReport);
|
||||
searchParams.set('report_id', processInstanceReport.id.toString());
|
||||
setSearchParams(searchParams);
|
||||
};
|
||||
|
||||
const saveAsReportComponent = () => {
|
||||
@ -926,7 +937,9 @@ export default function ProcessInstanceListTable({
|
||||
endToSeconds,
|
||||
} = calculateStartAndEndSeconds(false);
|
||||
|
||||
if (!valid || !reportMetadata) {
|
||||
const newReportMetadata = getNewReportMetadataBasedOnPageWidgets();
|
||||
|
||||
if (!valid || !reportMetadata || !newReportMetadata) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
@ -940,7 +953,7 @@ export default function ProcessInstanceListTable({
|
||||
processInitiatorSelection={processInitiatorSelection}
|
||||
processStatusSelection={processStatusSelection}
|
||||
processInstanceReportSelection={processInstanceReportSelection}
|
||||
reportMetadata={reportMetadata}
|
||||
reportMetadata={newReportMetadata}
|
||||
startFromSeconds={startFromSeconds}
|
||||
startToSeconds={startToSeconds}
|
||||
endFromSeconds={endFromSeconds}
|
||||
@ -1052,15 +1065,15 @@ export default function ProcessInstanceListTable({
|
||||
{ filter_field_value: '', filter_operator: '' }
|
||||
);
|
||||
if (reportColumn.filterable) {
|
||||
const reportFilter = getFilterByFromReportMetadata(
|
||||
reportColumnForEditing.accessor
|
||||
);
|
||||
if (reportFilter) {
|
||||
reportColumnForEditing.filter_field_value =
|
||||
reportFilter.field_value || '';
|
||||
reportColumnForEditing.filter_operator =
|
||||
reportFilter.operator || 'equals';
|
||||
}
|
||||
const reportFilter = getFilterByFromReportMetadata(
|
||||
reportColumnForEditing.accessor
|
||||
);
|
||||
if (reportFilter) {
|
||||
reportColumnForEditing.filter_field_value =
|
||||
reportFilter.field_value || '';
|
||||
reportColumnForEditing.filter_operator =
|
||||
reportFilter.operator || 'equals';
|
||||
}
|
||||
}
|
||||
return reportColumnForEditing;
|
||||
};
|
||||
@ -1622,6 +1635,8 @@ export default function ProcessInstanceListTable({
|
||||
<ProcessInstanceReportSearch
|
||||
onChange={processInstanceReportDidChange}
|
||||
selectedItem={processInstanceReportSelection}
|
||||
selectedReportId={searchParams.get('report_id')}
|
||||
handleSetSelectedReportCallback={setProcessInstanceReportSelection}
|
||||
/>
|
||||
</Column>,
|
||||
];
|
||||
|
@ -5,7 +5,6 @@ import {
|
||||
FormLabel,
|
||||
// @ts-ignore
|
||||
} from '@carbon/react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { truncateString } from '../helpers';
|
||||
import { ProcessInstanceReport } from '../interfaces';
|
||||
import HttpService from '../services/HttpService';
|
||||
@ -14,32 +13,42 @@ type OwnProps = {
|
||||
onChange: (..._args: any[]) => any;
|
||||
selectedItem?: ProcessInstanceReport | null;
|
||||
titleText?: string;
|
||||
selectedReportId?: string | null;
|
||||
handleSetSelectedReportCallback?: Function;
|
||||
};
|
||||
|
||||
export default function ProcessInstanceReportSearch({
|
||||
selectedItem,
|
||||
onChange,
|
||||
selectedReportId,
|
||||
handleSetSelectedReportCallback,
|
||||
titleText = 'Process instance perspectives',
|
||||
}: OwnProps) {
|
||||
const [processInstanceReports, setProcessInstanceReports] = useState<
|
||||
ProcessInstanceReport[] | null
|
||||
>(null);
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
const reportId = searchParams.get('report_id');
|
||||
|
||||
useEffect(() => {
|
||||
const selectedReportIdAsNumber = Number(selectedReportId);
|
||||
|
||||
function setProcessInstanceReportsFromResult(
|
||||
result: ProcessInstanceReport[]
|
||||
) {
|
||||
setProcessInstanceReports(result);
|
||||
if (selectedReportId && handleSetSelectedReportCallback) {
|
||||
result.forEach((processInstanceReport: ProcessInstanceReport) => {
|
||||
if (processInstanceReport.id === selectedReportIdAsNumber) {
|
||||
handleSetSelectedReportCallback(processInstanceReport);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/process-instances/reports`,
|
||||
successCallback: setProcessInstanceReportsFromResult,
|
||||
});
|
||||
}, [reportId]);
|
||||
}, [handleSetSelectedReportCallback, selectedReportId]);
|
||||
|
||||
const reportSelectionString = (
|
||||
processInstanceReport: ProcessInstanceReport
|
||||
|
@ -8,7 +8,6 @@ import ProcessGroupEdit from './ProcessGroupEdit';
|
||||
import ProcessModelShow from './ProcessModelShow';
|
||||
import ProcessModelEditDiagram from './ProcessModelEditDiagram';
|
||||
import ProcessInstanceList from './ProcessInstanceList';
|
||||
import ProcessInstanceReportShow from './ProcessInstanceReportShow';
|
||||
import ProcessModelNew from './ProcessModelNew';
|
||||
import ProcessModelEdit from './ProcessModelEdit';
|
||||
import ProcessInstanceShow from './ProcessInstanceShow';
|
||||
@ -88,10 +87,6 @@ export default function AdminRoutes() {
|
||||
path="process-instances/reports"
|
||||
element={<ProcessInstanceReportList />}
|
||||
/>
|
||||
<Route
|
||||
path="process-instances/reports/:report_identifier"
|
||||
element={<ProcessInstanceReportShow />}
|
||||
/>
|
||||
<Route
|
||||
path="process-instances/reports/new"
|
||||
element={<ProcessInstanceReportNew />}
|
||||
|
@ -1,97 +0,0 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams, useSearchParams } from 'react-router-dom';
|
||||
|
||||
// @ts-ignore
|
||||
import { Button, Table } from '@carbon/react';
|
||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||
|
||||
import PaginationForTable from '../components/PaginationForTable';
|
||||
import HttpService from '../services/HttpService';
|
||||
import { getPageInfoFromSearchParams } from '../helpers';
|
||||
|
||||
const PER_PAGE_FOR_PROCESS_INSTANCE_REPORT = 500;
|
||||
|
||||
export default function ProcessInstanceReport() {
|
||||
const params = useParams();
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
const [processInstances, setProcessInstances] = useState([]);
|
||||
const [reportMetadata, setReportMetadata] = useState({});
|
||||
const [pagination, setPagination] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const processResult = (result: any) => {
|
||||
const processInstancesFromApi = result.results;
|
||||
setProcessInstances(processInstancesFromApi);
|
||||
setReportMetadata(result.report_metadata);
|
||||
setPagination(result.pagination);
|
||||
};
|
||||
|
||||
function getProcessInstances() {
|
||||
const { page, perPage } = getPageInfoFromSearchParams(
|
||||
searchParams,
|
||||
PER_PAGE_FOR_PROCESS_INSTANCE_REPORT
|
||||
);
|
||||
let query = `?page=${page}&per_page=${perPage}`;
|
||||
searchParams.forEach((value, key) => {
|
||||
if (key !== 'page' && key !== 'per_page') {
|
||||
query += `&${key}=${value}`;
|
||||
}
|
||||
});
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/process-instances/reports/${params.report_identifier}${query}`,
|
||||
successCallback: processResult,
|
||||
});
|
||||
}
|
||||
|
||||
getProcessInstances();
|
||||
}, [searchParams, params]);
|
||||
|
||||
const buildTable = () => {
|
||||
const headers = (reportMetadata as any).columns.map((column: any) => {
|
||||
return <th>{(column as any).Header}</th>;
|
||||
});
|
||||
|
||||
const rows = processInstances.map((row) => {
|
||||
const currentRow = (reportMetadata as any).columns.map((column: any) => {
|
||||
return <td>{(row as any)[column.accessor]}</td>;
|
||||
});
|
||||
return <tr key={(row as any).id}>{currentRow}</tr>;
|
||||
});
|
||||
return (
|
||||
<Table striped bordered>
|
||||
<thead>
|
||||
<tr>{headers}</tr>
|
||||
</thead>
|
||||
<tbody>{rows}</tbody>
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
if (pagination) {
|
||||
const { page, perPage } = getPageInfoFromSearchParams(
|
||||
searchParams,
|
||||
PER_PAGE_FOR_PROCESS_INSTANCE_REPORT
|
||||
);
|
||||
return (
|
||||
<main>
|
||||
<ProcessBreadcrumb
|
||||
hotCrumbs={[['Process Groups', '/admin'], ['Process Instance']]}
|
||||
/>
|
||||
<h1>Process Instance Perspective: {params.report_identifier}</h1>
|
||||
<Button
|
||||
href={`/admin/process-instances/reports/${params.report_identifier}/edit`}
|
||||
>
|
||||
Edit process instance perspective
|
||||
</Button>
|
||||
<PaginationForTable
|
||||
page={page}
|
||||
perPage={perPage}
|
||||
pagination={pagination}
|
||||
tableToDisplay={buildTable()}
|
||||
/>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user