2022-10-12 10:21:49 -04:00
import { useContext , useEffect , useState } from 'react' ;
2022-11-07 18:37:46 -05:00
import { Link , useParams } from 'react-router-dom' ;
2022-10-31 15:09:21 -04:00
// @ts-ignore
2022-11-07 17:32:29 -05:00
import { Add , Upload } from '@carbon/icons-react' ;
import {
Accordion ,
AccordionItem ,
Dropdown ,
Button ,
Stack ,
ButtonSet ,
Modal ,
FileUploader ,
2022-11-07 17:41:35 -05:00
Table ,
TableHead ,
TableHeader ,
TableRow ,
TableCell ,
TableBody ,
2022-11-07 17:32:29 -05:00
// @ts-ignore
} from '@carbon/react' ;
2022-10-12 10:21:49 -04:00
import ProcessBreadcrumb from '../components/ProcessBreadcrumb' ;
import HttpService from '../services/HttpService' ;
import ErrorContext from '../contexts/ErrorContext' ;
2022-11-07 18:37:46 -05:00
import { ProcessFile , ProcessModel , RecentProcessModel } from '../interfaces' ;
interface ProcessModelFileCarbonDropdownItem {
label : string ;
action : string ;
processModelFile : ProcessFile ;
}
2022-10-12 10:21:49 -04:00
const storeRecentProcessModelInLocalStorage = (
processModelForStorage : any ,
params : any
) = > {
if (
params . process_group_id === undefined ||
params . process_model_id === undefined
) {
return ;
}
// All values stored in localStorage are strings.
// Grab our recentProcessModels string from localStorage.
const stringFromLocalStorage = window . localStorage . getItem (
'recentProcessModels'
) ;
// adapted from https://stackoverflow.com/a/59424458/6090676
// If that value is null (meaning that we've never saved anything to that spot in localStorage before), use an empty array as our array. Otherwise, use the value we parse out.
let array : RecentProcessModel [ ] = [ ] ;
if ( stringFromLocalStorage !== null ) {
// Then parse that string into an actual value.
array = JSON . parse ( stringFromLocalStorage ) ;
}
// Here's the value we want to add
const value = {
processGroupIdentifier : processModelForStorage.process_group_id ,
processModelIdentifier : processModelForStorage.id ,
processModelDisplayName : processModelForStorage.display_name ,
} ;
// If our parsed/empty array doesn't already have this value in it...
const matchingItem = array . find (
( item ) = >
item . processGroupIdentifier === value . processGroupIdentifier &&
item . processModelIdentifier === value . processModelIdentifier
) ;
if ( matchingItem === undefined ) {
// add the value to the beginning of the array
array . unshift ( value ) ;
// Keep the array to 3 items
if ( array . length > 3 ) {
array . pop ( ) ;
}
// turn the array WITH THE NEW VALUE IN IT into a string to prepare it to be stored in localStorage
const stringRepresentingArray = JSON . stringify ( array ) ;
// and store it in localStorage as "recentProcessModels"
window . localStorage . setItem ( 'recentProcessModels' , stringRepresentingArray ) ;
}
} ;
export default function ProcessModelShow() {
const params = useParams ( ) ;
const setErrorMessage = ( useContext as any ) ( ErrorContext ) [ 1 ] ;
2022-11-07 17:32:29 -05:00
const [ processModel , setProcessModel ] = useState < ProcessModel | null > ( null ) ;
2022-10-12 10:21:49 -04:00
const [ processInstanceResult , setProcessInstanceResult ] = useState ( null ) ;
2022-11-07 17:32:29 -05:00
const [ reloadModel , setReloadModel ] = useState < boolean > ( false ) ;
const [ filesToUpload , setFilesToUpload ] = useState < any > ( null ) ;
const [ showFileUploadModal , setShowFileUploadModal ] =
useState < boolean > ( false ) ;
2022-10-12 10:21:49 -04:00
useEffect ( ( ) = > {
2022-11-07 17:32:29 -05:00
const processResult = ( result : ProcessModel ) = > {
2022-10-12 10:21:49 -04:00
setProcessModel ( result ) ;
setReloadModel ( false ) ;
storeRecentProcessModelInLocalStorage ( result , params ) ;
} ;
HttpService . makeCallToBackend ( {
path : ` /process-models/ ${ params . process_group_id } / ${ params . process_model_id } ` ,
successCallback : processResult ,
} ) ;
} , [ params , reloadModel ] ) ;
const processModelRun = ( processInstance : any ) = > {
2022-10-18 16:41:13 -04:00
setErrorMessage ( null ) ;
2022-10-12 10:21:49 -04:00
HttpService . makeCallToBackend ( {
path : ` /process-models/ ${ params . process_group_id } / ${ params . process_model_id } /process-instances/ ${ processInstance . id } /run ` ,
successCallback : setProcessInstanceResult ,
failureCallback : setErrorMessage ,
httpMethod : 'POST' ,
} ) ;
} ;
const processInstanceCreateAndRun = ( ) = > {
HttpService . makeCallToBackend ( {
path : ` /process-models/ ${ params . process_group_id } / ${ params . process_model_id } /process-instances ` ,
successCallback : processModelRun ,
httpMethod : 'POST' ,
} ) ;
} ;
2022-11-07 17:32:29 -05:00
const processInstanceResultTag = ( ) = > {
if ( processModel && processInstanceResult ) {
let takeMeToMyTaskBlurb = null ;
// FIXME: ensure that the task is actually for the current user as well
const processInstanceId = ( processInstanceResult as any ) . id ;
const nextTask = ( processInstanceResult as any ) . next_task ;
if ( nextTask && nextTask . state === 'READY' ) {
takeMeToMyTaskBlurb = (
< span >
You have a task to complete . Go to { ' ' }
< Link to = { ` /tasks/ ${ processInstanceId } / ${ nextTask . id } ` } >
my task
< / Link >
.
< / span >
) ;
}
return (
< div className = "alert alert-success" role = "alert" >
< p >
Process Instance { processInstanceId } kicked off (
< Link
2022-11-08 09:00:51 -05:00
to = { ` /admin/process-models/ ${ processModel . process_group_id } / ${ processModel . id } /process-instances/ ${ processInstanceId } ` }
2022-11-07 17:32:29 -05:00
data - qa = "process-instance-show-link"
>
view
< / Link >
) . { takeMeToMyTaskBlurb }
< / p >
< / div >
2022-10-12 10:21:49 -04:00
) ;
}
2022-11-07 17:32:29 -05:00
return null ;
} ;
2022-10-12 10:21:49 -04:00
const onUploadedCallback = ( ) = > {
setReloadModel ( true ) ;
} ;
2022-11-07 18:37:46 -05:00
// Remove this code from
const onDeleteFile = ( fileName : string ) = > {
const url = ` /process-models/ ${ params . process_group_id } / ${ params . process_model_id } /files/ ${ fileName } ` ;
const httpMethod = 'DELETE' ;
const reloadModelOhYeah = ( _httpResult : any ) = > {
setReloadModel ( ! reloadModel ) ;
} ;
HttpService . makeCallToBackend ( {
path : url ,
successCallback : reloadModelOhYeah ,
httpMethod ,
} ) ;
} ;
const onProcessModelFileAction = ( selection : any ) = > {
const { selectedItem } = selection ;
if ( selectedItem . action === 'delete' ) {
onDeleteFile ( selectedItem . processModelFile . name ) ;
}
} ;
2022-10-12 10:21:49 -04:00
const processModelFileList = ( ) = > {
2022-11-08 09:00:51 -05:00
if ( ! processModel ) {
return null ;
}
2022-10-12 10:21:49 -04:00
let constructedTag ;
2022-11-08 09:00:51 -05:00
const tags = processModel . files . map (
( processModelFile : any , index : number ) = > {
const isPrimaryBpmnFile =
processModelFile . name === processModel . primary_file_name ;
const items : ProcessModelFileCarbonDropdownItem [ ] = [
{
label : 'Delete' ,
action : 'delete' ,
processModelFile ,
} ,
] ;
if ( processModelFile . name . match ( /\.bpmn$/ ) && ! isPrimaryBpmnFile ) {
items . push ( {
label : 'Mark as Primary' ,
action : 'mark_as_primary' ,
processModelFile ,
} ) ;
2022-10-12 10:21:49 -04:00
}
2022-11-08 09:00:51 -05:00
let actionDropDirection = 'bottom' ;
if ( index + 1 === processModel . files . length ) {
2022-11-08 09:50:58 -05:00
actionDropDirection = 'bottom' ;
2022-11-08 09:00:51 -05:00
}
// The dropdown will get covered up if it extends passed the table container.
// Using bottom direction at least gives a scrollbar so use that and hopefully
// carbon will give access to z-index at some point.
// https://github.com/carbon-design-system/carbon-addons-iot-react/issues/1487
const actionsTableCell = (
< TableCell key = { ` ${ processModelFile . name } -cell ` } >
< Dropdown
id = "default"
direction = { actionDropDirection }
label = "Select an action"
onChange = { ( e : any ) = > {
onProcessModelFileAction ( e ) ;
} }
items = { items }
itemToString = { ( item : ProcessModelFileCarbonDropdownItem ) = >
item ? item . label : ''
}
/ >
< / TableCell >
2022-10-12 10:21:49 -04:00
) ;
2022-11-08 09:00:51 -05:00
if ( processModelFile . name . match ( /\.(dmn|bpmn)$/ ) ) {
let primarySuffix = '' ;
if ( isPrimaryBpmnFile ) {
primarySuffix = '- Primary File' ;
}
constructedTag = (
< TableRow key = { processModelFile . name } >
< TableCell key = { ` ${ processModelFile . name } -cell ` } >
< Link
to = { ` /admin/process-models/ ${ processModel . process_group_id } / ${ processModel . id } /files/ ${ processModelFile . name } ` }
>
{ processModelFile . name }
< / Link >
{ primarySuffix }
< / TableCell >
{ actionsTableCell }
< / TableRow >
) ;
} else if ( processModelFile . name . match ( /\.(json|md)$/ ) ) {
constructedTag = (
< TableRow key = { processModelFile . name } >
< TableCell key = { ` ${ processModelFile . name } -cell ` } >
< Link
to = { ` /admin/process-models/ ${ processModel . process_group_id } / ${ processModel . id } /form/ ${ processModelFile . name } ` }
>
{ processModelFile . name }
< / Link >
< / TableCell >
{ actionsTableCell }
< / TableRow >
) ;
} else {
constructedTag = (
< TableRow key = { processModelFile . name } >
< TableCell key = { ` ${ processModelFile . name } -cell ` } >
2022-11-07 17:41:35 -05:00
{ processModelFile . name }
2022-11-08 09:00:51 -05:00
< / TableCell >
< / TableRow >
) ;
}
return constructedTag ;
2022-10-12 10:21:49 -04:00
}
2022-11-08 09:00:51 -05:00
) ;
2022-10-12 10:21:49 -04:00
2022-11-07 17:41:35 -05:00
// return <ul>{tags}</ul>;
2022-11-07 18:37:46 -05:00
const headers = [ 'Name' , 'Actions' ] ;
2022-11-07 17:41:35 -05:00
return (
< Table size = "lg" useZebraStyles = { false } >
< TableHead >
< TableRow >
{ headers . map ( ( header ) = > (
< TableHeader id = { header } key = { header } >
{ header }
< / TableHeader >
) ) }
< / TableRow >
< / TableHead >
< TableBody > { tags } < / TableBody >
< / Table >
) ;
2022-10-12 10:21:49 -04:00
} ;
const processInstancesUl = ( ) = > {
2022-11-08 09:00:51 -05:00
if ( ! processModel ) {
return null ;
}
2022-10-12 10:21:49 -04:00
return (
< ul >
< li >
< Link
2022-11-08 09:00:51 -05:00
to = { ` /admin/process-instances?process_group_identifier= ${ processModel . process_group_id } &process_model_identifier= ${ processModel . id } ` }
2022-10-12 10:21:49 -04:00
data - qa = "process-instance-list-link"
>
List
< / Link >
< / li >
< li >
< Link
2022-11-08 09:00:51 -05:00
to = { ` /admin/process-models/ ${ processModel . process_group_id } / ${ processModel . id } /process-instances/reports ` }
2022-10-12 10:21:49 -04:00
data - qa = "process-instance-reports-link"
>
Reports
< / Link >
< / li >
< / ul >
) ;
} ;
2022-11-07 17:32:29 -05:00
const handleFileUploadCancel = ( ) = > {
setShowFileUploadModal ( false ) ;
} ;
const handleFileUpload = ( event : any ) = > {
2022-11-08 09:00:51 -05:00
if ( processModel ) {
event . preventDefault ( ) ;
const url = ` /process-models/ ${ processModel . process_group_id } / ${ processModel . id } /files ` ;
const formData = new FormData ( ) ;
formData . append ( 'file' , filesToUpload [ 0 ] ) ;
formData . append ( 'fileName' , filesToUpload [ 0 ] . name ) ;
HttpService . makeCallToBackend ( {
path : url ,
successCallback : onUploadedCallback ,
httpMethod : 'POST' ,
postBody : formData ,
} ) ;
setShowFileUploadModal ( false ) ;
}
2022-11-07 17:32:29 -05:00
} ;
const fileUploadModal = ( ) = > {
return (
< Modal
open = { showFileUploadModal }
modalHeading = "Upload File"
primaryButtonText = "Upload"
secondaryButtonText = "Cancel"
onSecondarySubmit = { handleFileUploadCancel }
onRequestClose = { handleFileUploadCancel }
onRequestSubmit = { handleFileUpload }
>
< FileUploader
labelTitle = "Upload files"
labelDescription = "Max file size is 500mb. Only .bpmn, .dmn, and .json files are supported."
buttonLabel = "Add file"
buttonKind = "primary"
size = "md"
filenameStatus = "edit"
role = "button"
accept = { [ '.bpmn' , '.dmn' , '.json' ] }
disabled = { false }
iconDescription = "Delete file"
name = ""
multiple = { false }
onChange = { ( event : any ) = > setFilesToUpload ( event . target . files ) }
/ >
< / Modal >
) ;
} ;
2022-10-12 10:21:49 -04:00
const processModelButtons = ( ) = > {
2022-11-08 09:00:51 -05:00
if ( ! processModel ) {
return null ;
}
2022-10-12 10:21:49 -04:00
return (
2022-11-07 17:32:29 -05:00
< Accordion >
< AccordionItem
title = {
< Stack orientation = "horizontal" >
< span >
< Button size = "sm" kind = "ghost" >
Files
< / Button >
< / span >
< / Stack >
}
2022-10-12 10:21:49 -04:00
>
2022-11-07 17:32:29 -05:00
< ButtonSet >
< Button
renderIcon = { Upload }
onClick = { ( ) = > setShowFileUploadModal ( true ) }
size = "sm"
kind = ""
className = "button-white-background"
>
Upload File
< / Button >
2022-11-08 09:50:58 -05:00
< Button
renderIcon = { Add }
href = { ` /admin/process-models/ ${ processModel . process_group_id } / ${ processModel . id } /files?file_type=bpmn ` }
size = "sm"
>
New BPMN File
< / Button >
2022-11-07 17:32:29 -05:00
< Button
renderIcon = { Add }
2022-11-08 09:00:51 -05:00
href = { ` /admin/process-models/ ${ processModel . process_group_id } / ${ processModel . id } /files?file_type=dmn ` }
2022-11-07 17:32:29 -05:00
size = "sm"
>
New DMN File
< / Button >
< Button
renderIcon = { Add }
2022-11-08 09:00:51 -05:00
href = { ` /admin/process-models/ ${ processModel . process_group_id } / ${ processModel . id } /form?file_ext=json ` }
2022-11-07 17:32:29 -05:00
size = "sm"
>
New JSON File
< / Button >
< Button
renderIcon = { Add }
2022-11-08 09:00:51 -05:00
href = { ` /admin/process-models/ ${ processModel . process_group_id } / ${ processModel . id } /form?file_ext=md ` }
2022-11-07 17:32:29 -05:00
size = "sm"
>
New Markdown File
< / Button >
< / ButtonSet >
< br / >
{ processModelFileList ( ) }
< / AccordionItem >
< / Accordion >
2022-10-12 10:21:49 -04:00
) ;
} ;
2022-11-07 17:32:29 -05:00
if ( processModel ) {
2022-10-12 10:21:49 -04:00
return (
< >
2022-11-08 09:50:58 -05:00
{ fileUploadModal ( ) }
2022-10-12 10:21:49 -04:00
< ProcessBreadcrumb
2022-11-07 17:32:29 -05:00
processGroupId = { processModel . process_group_id }
processModelId = { processModel . id }
2022-10-12 10:21:49 -04:00
/ >
2022-11-07 18:37:46 -05:00
< h1 > { processModel . display_name } < / h1 >
< p > { processModel . description } < / p >
< Stack orientation = "horizontal" gap = { 3 } >
< Button onClick = { processInstanceCreateAndRun } variant = "primary" >
Run
< / Button >
< Button
2022-11-08 09:00:51 -05:00
href = { ` /admin/process-models/ ${ processModel . process_group_id } / ${ processModel . id } /edit ` }
2022-11-07 18:37:46 -05:00
variant = "secondary"
>
Edit process model
< / Button >
< / Stack >
2022-10-12 10:21:49 -04:00
< br / >
2022-11-08 09:50:58 -05:00
< br / >
{ processInstanceResultTag ( ) }
2022-10-12 10:21:49 -04:00
{ processModelButtons ( ) }
< br / >
< br / >
< h3 > Process Instances < / h3 >
{ processInstancesUl ( ) }
< / >
) ;
}
return null ;
}